おひさしぶりです、ritouです。
今日は家で風邪治してましたが、TLに流れてきた次世代なんちゃらの話題に乗っかって、ざっくりとした仕様紹介です。
- ベンダー個別のパスワード管理には課税せよ!? 次世代Webカンファレンス『identity』 #nextwebconf #nextwebconf407 - Togetter
- OAuth PKCEがRFC7636として発行されました。 | @_Nat Zone
- RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
一言でいうと
この規格はOAuth 2.0 [RFC6749]のPublic Client の Code Interception Attack 脆弱性に対応するもので、ephemeral keyを生成して、これを使ったProof of Possession of Key をします。
OAuth PKCEがRFC7636として発行されました。 | @_Nat Zone
なんですが、RFCの構成を意識して書いていきます。
問題
RFCでは、Introductionに書いてあります。
OAuth 2.0 [RFC6749] public clients are susceptible to the authorization code interception attack.
RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
Public Clientってのは、client_secretを安全に管理できないclientですね。
そういうclientはimplicitでは?って思われる人もいるかもしれませんが、落ち着きましょう。
Public ClientがAuthZ Codeを使う場合、次のようなフローになります。
- Clientは外部ブラウザを立ち上げるなりして、AuthZ Reqを送る
- AuthZ Serverに送られ、ログインとか何とかした後にAuthZ CodeがブラウザからClientに返される
- Clientは、AuthZ CodeとClient CredentialをAuthZ ServerのToken Endpointに送って、Access Tokenを取得する
これでPublic Clientの場合にどこがどうなっててどう危ないか、後ろから見ていくとわかるものです。
- Access Tokenを取得するためには、AuthZ CodeとClient Credentialが必要!
- Public Clientなので、Client Credentialを知ることができる。つまり、AuthZ CodeがあればAccess Tokenを取得できる!
- もし、カスタムURIスキームとか使ってAuthZ Codeの受け渡しのときに攻撃者にAuthZ Codeが渡ったら...AccessTokenを取得される問題!
ということですね。
解決方法
こちらもIntroductionに書いてあります。
To mitigate this attack, this extension utilizes a dynamically
RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
created cryptographically random key called "code verifier". A
unique code verifier is created for every authorization request, and
its transformed value, called "code challenge", is sent to the
authorization server to obtain the authorization code. The
authorization code obtained is then sent to the token endpoint with
the "code verifier", and the server compares it with the previously
received request code so that it can perform the proof of possession
of the "code verifier" by the client. This works as the mitigation
since the attacker would not know this one-time key, since it is sent
over TLS and cannot be intercepted.
ここでは翻訳したいわけじゃないので...
- Clientはcode verifierっていう、動的に生成されるcryptographically random key(←妥協)を生成
- code verifierは毎回AuthZ Req毎に生成される
- Clientは(code verifierを変換した)code challengeというパラメータをAuthZ Reqに含み、AuthZ ServerのAuthZ Endpointに送る
- AuthZ ServerはAuthZ Codeとcode challengeを紐づけつつ、Clientのredirect_uriにAuthZ Codeを送る
- Clientは、AuthZ Codeとcode verifierを一緒にAuthZ ServerのToken Endpointに送る
- AuthZ ServerはAuthZ Codeと紐づけておいたcode challengeとcode verifierの値を検証し、Access Tokenを返す
- 攻撃者はcode verifier知らないのでAuthZ Codeを奪うだけではAccess Token取得できない
こんな感じです。
ここまでで大事なことは書いたので、後はざっと流しましょう。
用語
- code verifier : 動的に生成されたcryptographically random stringであり、Token EndpointへのAccess Token Reqに含まれる
- code challenge : code verifierから生成されたchallengeの値で、AuthZ EndpointへのAuthZ Reqに含まれる
- code challenge method : code verifierからcode challengeを作る時の方法
- Base64url Encoding : 省略
code verifierの生成
最小43文字、最大128文字の...
code_verifier = high-entropy cryptographic random STRING using the
unreserved characters [A-Z] / [a-z] / [0-9] / "-" / "." / "_" / "~"
from Section 2.3 of [RFC3986], with a minimum length of 43 characters
and a maximum length of 128 characters.ABNF for "code_verifier" is as follows.
code-verifier = 43*128unreserved
RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39
code challengeの生成
code challengeの生成方法について、plainとS256が記載されています。
plain
RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
code_challenge = code_verifier
S256
code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
ABNF for "code_challenge" is as follows.
code-challenge = 43*128unreserved
RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~"
ALPHA = %x41-5A / %x61-7A
DIGIT = %x30-39
ClientはS256できるならS256使う。どうしようもなかったらplain。
AuthZ Reqへの追加パラメータ
code challengeとmethodを指定します。
code_challenge
RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
REQUIRED. Code challenge.
code_challenge_method
OPTIONAL, defaults to "plain" if not present in the request. Code
verifier transformation method is "S256" or "plain".
AuthZ Resについて
AuthZ ServerはAuthZ Codeとcode_challenge, code_challenge_methodは返してはいけません。
method=plainだったらcode verifierそのものですからね。
Access Token Reqへの追加パラメータ
code_verifier
RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
REQUIRED. Code verifier
AuthZ Serverの検証について
- code_challenge_methodの値に従って、code_verifierそのままもしくはs256した値とcode_challengeの値を比較する
Security Considerations
は、読んでおきましょう(疲れた)