MS_OAuthPKCE - NetDevInfraWGinOSSConsortium/NetDevInfraWiki GitHub Wiki
- 戻る(OAuth 2.0 拡張)
- OAuth PKCE
- OAuth 2.0
- OAuth 2.1
- OpenID Connect
認可コード横取り攻撃(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_idやclient_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_methodとcode_challengeを使用して、
code_verifierの妥当性を検証する。 -
なお、このフローでは、
client_secretは不要とする。
補足(なぜこれで防げるのか): 要点は
「code_challenge(ハッシュ)は公開しても、
code_verifier(原文)は攻撃者が知り得ない」という一点にある。攻撃者が認可コードを横取りしても、
トークン交換にはcode_verifierが要る。
それは正規アプリのメモリ内にしか無いため、コードだけでは交換できない。ハッシュの一方向性がそのまま防御になっている、という単純で強い仕組み。
- 認可エンドポイント
| # | パラメタ | 要否 | 説明 |
|---|---|---|---|
| 1 | code_challenge |
必須 | Code Verifier を元に計算された Code Challenge の値 |
| 2 | code_challenge_method |
任意 | Code Challenge の計算に用いるハッシュ関数。plain と S256 があり、デフォルトは 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.1 はS256を要求)また、サーバー側は「
code_challengeが付いていない認可リクエスト」も
拒否する必要がある。PKCE ダウングレード攻撃(攻撃者が
code_challengeを取り除く)を防ぐためである。
-
サーバー・ステートレス
通常、code_challengeおよびcode_challenge_methodの値は
暗号化された形式でcode自体に格納される。 -
サーバー・ステートフル
codeに関連付けられた Authorization Server に格納することもできるが、
code_challengeとcodeを関連付ける方法は、仕様の対象外。
- 暗号論的に安全な乱数生成器で生成すること(推測可能では意味が無い)。
- 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 における 認証を参照)。
- RFC 7636 - Proof Key for Code Exchange by OAuth Public Clients
https://datatracker.ietf.org/doc/html/rfc7636 - RFC 8252 - OAuth 2.0 for Native Apps
https://datatracker.ietf.org/doc/html/rfc8252 - UserAgentでOAuth2のTokenを取得するベスト・プラクティス
Tags: 移行, IT国際標準, 認証基盤, クレームベース認証, OAuth