Secure Steady
PKCE - PKCE(認可コード横取り対策) の使い方・オプション・サンプル

PKCE - PKCE(認可コード横取り対策)

SPA・モバイルアプリ向けの拡張フロー。code_verifier / code_challenge で認可コード横取りを防止する。

概念図

PKCE(認可コード横取り対策) diagram

実例

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_wW1gFWFOEjXk

PKCE の仕組み

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 フローの流れ

  1. クライアントが code_verifier をランダム生成し、そのハッシュ値 code_challenge を計算する
  2. 認可リクエストに code_challengecode_challenge_method=S256 を付与して認可サーバーに送信する
  3. 認可サーバーは code_challenge を保存し、認可コードを発行する
  4. クライアントがトークンエンドポイントに認可コードと元の code_verifier を送信する
  5. 認可サーバーが 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 を必須とする設定を有効にすることで、クライアント実装の不備によるセキュリティリスクを低減できます。

関連トピック