PKCE による認可コード保護
スマートフォンのネイティブアプリが,OAuth 2.0 の認可コードグラントに PKCE(code_challenge_method は S256)を組み合わせて利用している。攻撃者は,同じカスタム URI スキームを登録した不正アプリによって認可レスポンスの認可コードを横取りし,認可リクエストの内容も観測できた。client_id も攻撃者に知られている。攻撃者がこの認可コードを使ってトークンリクエストを送った結果として,適切なものはどれか。
- ア認可コードは有効期限内であれば提示者を問わず有効なので,攻撃者は認可コードと client_id を送るだけでアクセストークンを取得できる。
- イ攻撃者は,観測した code_challenge の値を code_verifier としてトークンリクエストに含めれば,アクセストークンを取得できる。
- ウ認可サーバは,認可レスポンスが不正アプリに渡った時点でリダイレクト URI の不一致を検出し,その認可コードを無効にする。
- エ攻撃者は code_verifier を知らず,code_challenge から求めることもできないので,トークンエンドポイントはエラーを返し,アクセストークンは発行されない。
正解と解説を見る
正解 エ
この問題の基礎知識
- OAuth 2.0 の認可コードグラントでは,クライアントは認可エンドポイントから認可コードを受け取り,それをトークンエンドポイントでアクセストークンと交換する。
- ネイティブアプリはクライアントシークレットを安全に保持できず,カスタム URI スキームは他のアプリも登録できるので,認可コードを横取りされ得る。
- PKCE では,クライアントが乱数の code_verifier を生成し,その変換値の code_challenge を認可リクエストに,code_verifier をトークンリクエストに含める。認可サーバは両者が対応するかを確認する。
- S256 では code_verifier の SHA-256 ハッシュ値を base64url 符号化して code_challenge とするので,ハッシュ関数の一方向性により code_challenge から code_verifier は求められない。plain では両者が同じ値になる。
PKCE を用いなければこの攻撃は成立し得るが,PKCE を用いた場合,認可コードは,認可リクエストの code_challenge に対応する code_verifier を併せて提示しなければトークンと交換できない。
S256 では,トークンエンドポイントが受け取った code_verifier の SHA-256 ハッシュ値を base64url 符号化して code_challenge と比較する。code_challenge をそのまま送っても一致しない。両者が同じ値になるのは plain の場合である。
不正アプリは正規のアプリと同じカスタム URI スキームを登録しており,認可サーバから見たリダイレクト URI は正規のものと変わらない。端末内でどのアプリが応答を受け取ったかを,認可サーバは知ることができない。
code_verifier は正規のアプリだけが保持し,認可リクエストにはそのハッシュ値から作った code_challenge だけが含まれる。ハッシュ値から code_verifier は求められないので,横取りした認可コードはトークンと交換できない。
照合の根拠(4 件)
- ウ(誤り): 不正アプリは同じカスタムスキームのハンドラとして登録できるので,認可サーバから見たリダイレクト URI は正規のものと変わらない
Note that it is possible for a malicious app to register itself as a handler for the custom scheme in addition to the legitimate OAuth 2.0 app.
RFC 7636 · Section 1(新しいタブで開きます) - エ(正解)・ア(誤り): code_verifier を持たない攻撃者は横取りした認可コードをトークンと交換できない
An attacker who intercepts the authorization code at (B) is unable to redeem it for an access token, as they are not in possession of the "code_verifier" secret.
RFC 7636 · Section 1.1(新しいタブで開きます) - イ(誤り): S256 では受け取った code_verifier のハッシュ値を code_challenge と比較するので,code_challenge をそのまま送っても一致しない(直接比較は plain の場合)
If the "code_challenge_method" from Section 4.3 was "S256", the received "code_verifier" is hashed by SHA-256, base64url-encoded, and then compared to the "code_challenge", i.e.:
RFC 7636 · Section 4.6(新しいタブで開きます) - エ(正解): 一致しなければトークンエンドポイントはエラー(invalid_grant)を返す
If the values are not equal, an error response indicating "invalid_grant" as described in Section 5.2 of [RFC6749] MUST be returned.
RFC 7636 · Section 4.6(新しいタブで開きます)
問題文では認可リクエストも観測されている(RFC 7636 1 章の前提 4b)が,S256 なので観測できるのは code_challenge だけで,SHA-256 の一方向性により code_verifier は求められない。