PKCE - PKCE(認可コード横取り対策)
SPA・モバイルアプリ向けの拡張フロー。code_verifier / code_challenge で認可コード横取りを防止する。
概念図
実例
code_verifier(ランダム文字列)と code_challenge(SHA-256 ハッシュ)の生成
bash
// code_verifier と code_challenge の生成
const array = new Uint8Array(32);
crypto.getRandomValues(array);
const code_verifier = btoa(String.fromCharCode(...array))
.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
const hashBuffer = await crypto.subtle.digest("SHA-256",
new TextEncoder().encode(code_verifier));
const code_challenge = btoa(
String.fromCharCode(...new Uint8Array(hashBuffer))
).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");PKCE 付き認可リクエスト(code_challenge と S256 メソッドを含む)
bash
GET /authorize?
response_type=code
&client_id=my-spa-client
&redirect_uri=https://app.example.com/callback
&scope=openid profile
&state=xyz123
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256トークン交換リクエスト(code_verifier を送信して検証させる)
bash
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https://app.example.com/callback
&client_id=my-spa-client
&code_verifier=dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXkPKCE の仕組み
PKCE(Proof Key for Code Exchange / RFC 7636)は、OAuth 2.0 認可コードフローにおける認可コード横取り攻撃を防ぐための拡張仕様です。
SPA やモバイルアプリなどのパブリッククライアントは、クライアントシークレットを安全に保管できないため、認可コードが第三者に傍受された場合にトークンを不正取得される危険があります。
code_verifier と code_challenge の関係
| 項目 | 説明 |
|---|---|
| code_verifier | クライアントが生成するランダムな文字列(43-128文字、Base64URL エンコード) |
| code_challenge | code_verifier の SHA-256 ハッシュを Base64URL エンコードしたもの |
| code_challenge_method | ハッシュ方式の指定。S256(SHA-256)を必ず使用する。plain は非推奨 |
PKCE フローの流れ
- クライアントが
code_verifierをランダム生成し、そのハッシュ値code_challengeを計算する - 認可リクエストに
code_challengeとcode_challenge_method=S256を付与して認可サーバーに送信する - 認可サーバーは
code_challengeを保存し、認可コードを発行する - クライアントがトークンエンドポイントに認可コードと元の
code_verifierを送信する - 認可サーバーが
code_verifierをハッシュ化し、保存していたcode_challengeと一致するか検証する
この仕組みにより、認可コードを傍受した攻撃者は code_verifier を知らないためトークン交換ができません。
SPA / モバイルアプリでの実装上の注意点
SPA(シングルページアプリケーション)
- code_verifier の保管:
sessionStorageに保存し、トークン交換完了後に削除する。localStorageはタブ間で共有されるため避ける - state パラメータとの併用: PKCE は認可コード横取りを防ぐが、CSRF 攻撃は防げない。
stateパラメータを併用して CSRF を防止する - Implicit フローからの移行: Implicit フロー(
response_type=token)はアクセストークンが URL フラグメントに露出するため非推奨。PKCE 付き認可コードフローに移行すること - リフレッシュトークンの利用: SPA ではリフレッシュトークンを使用する場合、Refresh Token Rotation を有効にして再利用を検知する
モバイルアプリ
- カスタム URL スキームの危険性:
myapp://callbackのようなカスタムスキームは、他のアプリに横取りされる可能性がある。iOS では Universal Links、Android では App Links を使用する - システムブラウザの使用: 認可画面は WebView ではなくシステムブラウザ(ASWebAuthenticationSession / Custom Tabs)で開く。WebView はトークンの窃取リスクがある
- 安全な乱数生成:
code_verifierの生成には OS 提供の暗号論的乱数生成器を使用する(Web Crypto API, SecRandomCopyBytes 等)
PKCE の適用範囲の拡大
OAuth 2.1 のドラフト仕様では、PKCE はパブリッククライアントだけでなくすべてのクライアント(コンフィデンシャルクライアント含む)に推奨されています。
サーバーサイドアプリであっても、認可コードが中間者攻撃で傍受されるリスクは存在します。
PKCE を追加コストなしで導入できるため、すべての OAuth 2.0 クライアントで有効にするのがベストプラクティスです。
認可サーバー側でも、PKCE を必須とする設定を有効にすることで、クライアント実装の不備によるセキュリティリスクを低減できます。
