MS_OAuthPKCE - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki

OAuth PKCE

概要

認可コード横取り攻撃(authorization code interception attack)への対策。

  • (Authorization Code グラント種別により発行された)
    認可コードをクライアントアプリケーションが受け取る際、
    悪意のあるアプリケーションがその認可コードを横取りする攻撃に対抗する仕様。
  • OpenID Connectの Authorization Code Flow とも
    組み合わせることができる。

補足(最新化:全クライアントで必須): 元は
ネイティブアプリ(Public クライアント)向けの対策として
RFC 7636(2015)で標準化された。

しかし現在は、OAuth 2.1 / RFC 9700 により
すべてのクライアント(Confidential 含む)で必須
になっている。

理由は、client_secret を持つ Web アプリでも、

  • 認可コードが Referer やログから漏れる
  • 認可コード インジェクション(攻撃者が自分のコードを被害者に注入する)

といった経路が残るため。
PKCE は「コードを取ってきた者と、交換する者が同一である」ことを
保証するので、これらを一律に塞げる。

課題

ネイティブアプリが、外部ブラウザを使用して OAuth 2.0 認証要求を発行する場合、

Implicit Flow ではなく、Authorization Code Flow を使用し、
redirect_uri に Private-Use URI Scheme を使用して code を取得する。」

という方式があるが(下図を参照)、

+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
| End Device (e.g., Smartphone)  |
|                                |
| +-------------+   +----------+ | (6) Access Token  +----------+
| |Legitimate   |   | Malicious|<--------------------|          |
| |OAuth 2.0 App|   | App      |-------------------->|          |
| +-------------+   +----------+ | (5) Authorization |          |
|        |    ^          ^       |        Grant      |          |
|        |     \         |       |                   |          |
|        |      \   (4)  |       |                   |          |
|    (1) |       \  Authz|       |                   |          |
|   Authz|        \ Code |       |                   |  Authz   |
| Request|         \     |       |                   |  Server  |
|        |          \    |       |                   |          |
|        |           \   |       |                   |          |
|        v            \  |       |                   |          |
| +----------------------------+ |                   |          |
| |                            | | (3) Authz Code    |          |
| |     Operating System/      |<--------------------|          |
| |         Browser            |-------------------->|          |
| |                            | | (2) Authz Request |          |
| +----------------------------+ |                   +----------+
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~+
  • Private-Use URI Scheme の上書き攻撃によって、この code が傍受されることがある。
  • また、client_idclient_secret も漏洩してしまう可能性がある。

補足: Private-Use URI Scheme(myapp://callback のような
独自スキーム)は、OS が「どのアプリがそのスキームを扱うか」を
一意に保証しない
。悪意あるアプリが同じスキームを登録すると、
認可コードがそちらに届いてしまう。

現在はこれに加えて、**Claimed HTTPS URI(App Links / Universal Links)**を
使うことが推奨されている(RFC 8252)。
ドメインの所有証明が必要なため、他アプリが横取りできない。

対策

  • コード交換のための証明鍵(PKCE、"pixy" と発音)を使用して脅威を軽減する。
  • プロビジョニングされたアプリケーションのバイナリ・ファイル中の client_secret
    ついては、機密性が考慮されていないため、client_secret を必要としない方式
    になっている。

仕様

仕様の概要

このフローは、

  • Client ≒ デバイス(UserAgent)になる。
  • redirect_uri に Private-Use URI Scheme が使用される点を除いて、
    Authorization Code Flow と同じだが、
    認証リクエストと Token リクエストにパラメタが追加される。
                                             +-------------------+
                                             |   Authz Server    |
   +--------+                                | +---------------+ |
   |        |--(A)- Authorization Request ---->|               | |
   |        |       + t(code_verifier), t_m  | | Authorization | |
   |        |                                | |    Endpoint   | |
   |        |<-(B)---- Authorization Code -----|               | |
   |        |                                | +---------------+ |
   | Client |                                |                   |
   |        |                                | +---------------+ |
   |        |--(C)-- Access Token Request ---->|               | |
   |        |          + code_verifier       | |    Token      | |
   |        |                                | |   Endpoint    | |
   |        |<-(D)------ Access Token ---------|               | |
   +--------+                                | +---------------+ |
                                             +-------------------+

仕様の詳細

  • Client は、認可リクエストを送信する前に、

    • 乱数 code_verifier を発生させ、
    • ハッシュ関数 t を用いたハッシュ値 t(code_verifier) を計算する。
  • Client は、認可リクエストに以下のパラメタを含める。

    • code_challenge_method:ハッシュ関数 t
    • code_challenge:ハッシュ値 t(code_verifier)
  • 認可エンドポイントで、前述の認可リクエストを受信した際、これらを保存しておく。

  • Client は、Token リクエストに、先程生成した code_verifier パラメタを含める。

  • Token エンドポイントで code_verifier を含む Token リクエストを受信した場合、
    保存されている code_challenge_methodcode_challenge を使用して、
    code_verifier の妥当性を検証する。

  • なお、このフローでは、client_secret は不要とする。

補足(なぜこれで防げるのか): 要点は
code_challenge(ハッシュ)は公開しても、
code_verifier(原文)は攻撃者が知り得ない
」という一点にある。

攻撃者が認可コードを横取りしても、
トークン交換には code_verifier が要る。
それは正規アプリのメモリ内にしか無いため、コードだけでは交換できない

ハッシュの一方向性がそのまま防御になっている、という単純で強い仕組み。

各エンドポイントで受け取るパラメタ

  • 認可エンドポイント
# パラメタ 要否 説明
1 code_challenge 必須 Code Verifier を元に計算された Code Challenge の値
2 code_challenge_method 任意 Code Challenge の計算に用いるハッシュ関数。plainS256 があり、デフォルトは plain
  • Token エンドポイント
# パラメタ 要否 説明
1 code_verifier 必須 動的に作成された暗号的にランダムな 43-128 文字の base64url 文字列

ハッシュ関数

内容
plain 恒等関数(引数と同じ戻り値を返す)。code_verifier をそのまま使用する(Client で S256 がサポートできない場合)。
S256 BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))

補足(plain は使わない): code_challenge_method
既定値が plain である点は罠である。
plain だと code_challenge = code_verifier なので、
認可リクエストを覗ければ code_verifier も分かり、意味が無い

  • クライアントは必ず code_challenge_method=S256 を明示する
  • サーバーは plain を拒否するOAuth 2.1S256 を要求)

また、サーバー側は「code_challenge が付いていない認可リクエスト」も
拒否する必要がある。PKCE ダウングレード攻撃(攻撃者が
code_challenge を取り除く)を防ぐためである。

code との関連

  • サーバー・ステートレス
    通常、code_challenge および code_challenge_method の値は
    暗号化された形式で code 自体に格納される。
  • サーバー・ステートフル
    code に関連付けられた Authorization Server に格納することもできるが、
    code_challengecode を関連付ける方法は、仕様の対象外。

セキュリティに関する考慮事項

code_verifier

  • 暗号論的に安全な乱数生成器で生成すること(推測可能では意味が無い)。
  • 43〜128 文字の base64url(= 256 bit のエントロピーが目安)。
  • リクエストごとに新規生成し、使い回さないこと。

補足: 生成例(C#)。

var bytes = RandomNumberGenerator.GetBytes(32);
var verifier = Base64UrlEncode(bytes);                       // code_verifier
var challenge = Base64UrlEncode(SHA256.HashData(
                    Encoding.ASCII.GetBytes(verifier)));     // code_challenge (S256)

なお、ASP.NET Core の AddOpenIdConnect()
UsePkce = true が既定であり、自前で実装する必要はない
ASP.NET Core における 認証を参照)。

参考


Tags: 移行, IT国際標準, 認証基盤, クレームベース認証, OAuth

⚠️ **GitHub.com Fallback** ⚠️