TL

OAuth 2.0 の認可フロー内部(Authorization Code + PKCE)

PKCE が code interception を、state/nonce が CSRF とリプレイを別々に塞ぐ原理を内部から理解し、Authorization Code + PKCE の選定と実装を誤らない。

応用OAuthPKCEOIDC認可CSRFセキュリティ最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.OAuth 2.0 はアクセストークンを「誰に・どう渡すか」のフロー集。Authorization Code はトークンを直接ブラウザに晒さず、認可コードをバックチャネルで交換するため最も安全。Implicit はトークンを URL フラグメントで直接返すため非推奨化された。
  • 2.PKCE は code_verifier(高エントロピー乱数)の SHA-256 ハッシュを code_challenge として先に送り、トークン交換時に元の verifier を提示させる。傍受された認可コードだけでは verifier を再現できず、code interception 攻撃が成立しない。
  • 3.state は認可リクエスト〜コールバックを紐付ける乱数で CSRF(強制ログイン)を防ぐ。nonce は ID トークンに埋め込ませる乱数で、古いトークンの使い回し(リプレイ)を検出する。両者は役割が別で併用する。

OAuth 2.0 は「トークンの渡し方」の規格

OAuth 2.0(RFC 6749)は、ユーザーのパスワードを第三者アプリに渡さずに、限定された権限(スコープ)を表すアクセストークンを安全に発行・委譲するための枠組みです。登場人物は4者あります。

  • リソースオーナー:本人(ユーザー)。
  • クライアント:トークンを使いたいアプリ(SPA・モバイル・サーバー)。
  • 認可サーバー:本人を認証し、コードやトークンを発行する。
  • リソースサーバー:トークンを検証して API を提供する。

OAuth 2.0 が**認可(Authorization)の規格である点に注意が必要です。「この人が誰か」を確実に伝える認証(Authentication)**は OAuth 2.0 単体の責務ではなく、その上に OpenID Connect(OIDC) を載せて ID トークンを得ます。両者の役割分担は 認証と認可の違い の通りで、OAuth でログインを実装するときは OIDC を併用するのが正道です。

「フロー(グラントタイプ)」とは、この4者の間でどの経路を通ってトークンを受け渡すかの手順の違いにすぎません。経路が違えば、傍受や CSRF に対する強度も変わります。

4つのフローの違い

代表的な4フローを、用途と安全性の観点で並べます。

フロー主な用途トークンの渡り方現在の評価
Authorization Code (+PKCE)Webアプリ・SPA・モバイル全般認可コードをバックチャネルでトークンに交換推奨(事実上の標準)
Implicit旧来のSPAトークンをURLフラグメントで直接返す非推奨(PKCE付きCodeへ移行)
Client Credentialsサーバー間(ユーザー不在)クライアント認証情報を直接トークンに交換用途が合えば適切
Device AuthorizationTV・CLI等の入力困難な端末別端末でコード入力、本体はポーリング該当端末で適切
  • Authorization Code:認可サーバーはまず短命の認可コードを返し、クライアントは**サーバー間の直接通信(バックチャネル)**でそのコードをトークンに交換します。トークンがブラウザの URL やフロントチャネルに露出しないのが安全性の核です。
  • Implicit:コード交換を省き、#access_token=... の形でトークンをフラグメントに直接返していました。ブラウザ履歴・Referer・拡張機能に漏れやすく、リフレッシュトークンも安全に扱えないため、現在は PKCE 付き Authorization Code への一本化が勧告されています。
  • Client Credentials:ユーザーが介在しないバッチやマイクロサービス間で、クライアント自身の client_id / client_secret を提示してトークンを得ます。人間の同意画面が無い点が他と決定的に違います。
  • Device Authorization:キーボードの無いテレビや CLI 向け。本体は device_code を取得して画面に短いユーザーコードを表示し、ユーザーはスマホ等の別端末で承認、本体はトークン発行をポーリングで待ちます。

PKCE が code interception を防ぐ原理

Authorization Code の弱点は、認可コードがリダイレクトでブラウザを経由することです。とくにモバイルや SPA では client_secret を秘匿できないため、悪意あるアプリが OS のカスタム URL スキームを横取りするなどして認可コードを傍受すると、それをトークンに交換できてしまいます。これが code interception(認可コード傍受)攻撃です。

PKCE(Proof Key for Code Exchange、RFC 7636) は、コードを横取りされても交換できなくする仕組みです。要は「コードを開始した本人だけが知る秘密」を後出しで証明させます。

1. クライアントが乱数を生成:    code_verifier  (43〜128文字の高エントロピー文字列)
2. その SHA-256 を Base64URL:   code_challenge = BASE64URL(SHA256(code_verifier))
3. 認可リクエストに同梱:        code_challenge と code_challenge_method=S256 を送る
   → 認可サーバーは challenge を認可コードに紐付けて記憶
4. コールバックで認可コード受領
5. トークン交換時に提示:        元の code_verifier を送る
   → サーバーは SHA256(verifier) == 記憶した challenge を検証。一致しなければ拒否

攻撃者が認可コードを傍受しても、code_verifier は最初のリクエストを送った正規クライアントのメモリ内にしか存在しません。通信路に流れるのはハッシュ済みの code_challenge だけで、SHA-256 は一方向関数なので challenge から verifier を復元できません。よって傍受したコードを交換しようとしても verifier を提示できず、サーバーが弾きます。

method は必ず S256。plain は避ける

PKCE には code_challenge_method=plain(challenge と verifier が同一)も規格上ありますが、これは challenge がそのまま秘密値になるため傍受で破られます。実装では必ず S256(SHA-256 ハッシュ) を使ってください。RFC 7636 も S256 を必須・plain を非推奨としています。

PKCE は当初モバイル向けでしたが、現在は public client(secret を持てない SPA・モバイル)だけでなく confidential client でも全フローに付けるべきとされ、OAuth 2.1 のドラフトでは Authorization Code に PKCE が常時必須化されています。

state による CSRF 対策

PKCE は「コードの横取り」を防ぎますが、**「攻撃者が用意したコードを被害者に握らせる」**別系統の攻撃には別の対策が要ります。

攻撃者が自分のアカウントで認可を開始し、得たコールバック URL を罠ページから被害者のブラウザに踏ませると、被害者のクライアントセッションに攻撃者のアカウントが結び付く(ログイン CSRF/アカウント混同)危険があります。これを防ぐのが state パラメータです。

1. 認可リクエスト直前にクライアントが乱数 state を生成し、セッションに保存
2. 認可リクエストに state=<乱数> を付けて送る
3. 認可サーバーはコールバックで同じ state をそのまま返す
4. クライアントは「返ってきた state == セッションに保存した state」を検証
   → 一致しなければ、自分が始めたフローではないとみなし破棄

state認可開始とコールバックが同一ブラウザ・同一セッション由来であることを保証する CSRF トークンそのものです。仕組みは CSRF(クロスサイトリクエストフォージェリ) のトークン照合と同型で、推測不能な乱数を「往復させて突き合わせる」点が共通します。state には戻り先 URL を持たせることもありますが、その場合も改ざん検知できる形で署名/照合する必要があります。

state 検証の省略は実害が出る

ライブラリ任せにせず、state を生成・保存・照合しているかを必ず確認してください。検証を省くと、ログイン CSRF や、悪意あるサイトへトークンを送らせる攻撃の入口になります。PKCE があっても state の役割は代替できません(守る対象が別だからです)。

nonce によるリプレイ対策(OIDC)

state が OAuth のフロー全体を守るのに対し、nonce は OIDC の ID トークンを守る値です。役割を取り違えないよう、両者を整理します。

守る対象防ぐ攻撃検証の仕方
state認可リクエスト〜コールバックの往復CSRF・ログイン強制・アカウント混同送った値と返った値の一致をクライアントが照合
nonceID トークン(OIDC)ID トークンのリプレイ(使い回し)送った値が ID トークンの nonce クレームと一致するか照合
PKCE認可コードcode interception(コード傍受)verifier のハッシュが challenge と一致するか認可サーバーが照合

OIDC では認可リクエストに nonce=<乱数> を載せます。認可サーバーは発行する ID トークンの nonce クレームに同じ値を埋め込んで署名します。クライアントは ID トークン検証時に「自分が送った nonce と一致するか」を確認します。これにより、過去に発行された ID トークンを攻撃者が再注入してもセッションを取れません——古いトークンの nonce は現在のリクエストと一致しないからです。

認可リクエスト:  ...&nonce=Xk29...&...
ID トークン(JWT) のペイロード例:
  {
    "iss": "https://issuer.example",
    "aud": "client-123",
    "nonce": "Xk29...",   ← 送った nonce と一致するか検証
    "exp": 1750000000,    ← 期限切れも必ず確認
    "iat": 1749996400
  }

ID トークンの検証では nonce に加えて iss(発行者)・aud(自分宛か)・exp(期限)・署名をすべて確認します。ここを緩めると、別クライアント宛のトークンの流用や期限切れトークンの受理を許します。フィッシング耐性まで踏み込むなら、認証要素そのものを公開鍵チャレンジにする FIDO2 / WebAuthn や、second factor の TOTP/HOTP と組み合わせる設計が有効です。

どのフローを選ぶか

判断は「ユーザーが介在するか」「secret を秘匿できるか」「端末が入力可能か」の3点でほぼ決まります。

状況選ぶフロー必須の付帯対策
SPA / モバイル(secret 不可)Authorization Code + PKCEPKCE(S256) + state、ログインなら nonce
サーバーサイド Web(secret 可)Authorization Code + PKCEPKCE + state + client_secret
ユーザー不在のサーバー間Client Credentialsclient認証情報の厳格な秘匿・スコープ最小化
TV・CLI 等の入力困難端末Device Authorizationuser_code の有効期限・ポーリング間隔遵守
旧来の Implicit を使用中Code + PKCE へ移行フラグメント返却の廃止

結論として、ユーザーが関わるフローは例外なく Authorization Code + PKCEを選び、state を必ず照合し、ログインを伴うなら OIDC の nonce を加える——これが現行の正解です。Implicit は新規採用せず、既存実装も移行対象です。PKCE がコード傍受を、state が CSRF を、nonce がリプレイを、それぞれ別々の角度から塞いでいる点を理解しておけば、ライブラリ設定の意味も、監査で何を見るべきかも明確になります。

セキュリティ Article

OAuth 2.0 の認可フロー内部(Authorization Code + PKCE)を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

OAuth

比較で見る軸

難易度: advanced / カテゴリ: セキュリティ / タグ数: 6

導入後に効く点

PKCE は code_verifier(高エントロピー乱数)の SHA-256 ハッシュを code_challenge として先に送り、トークン交換時に元の verifier を提示させる。傍受された認可コードだけでは verifier を再現できず、code interception 攻撃が成立しない。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
セキュリティ
タグ数
6

判断チェックリスト

  • 自社の用途が「OAuth / PKCE」に近いか確認する。
  • 強みである「OAuth 2.0 はアクセストークンを「誰に・どう渡すか」のフロー集。Authorization Code はトークンを直接ブラウザに晒さず、認可コードをバックチャネルで交換するため最も安全。Implicit はトークンを URL フラグメントで直接返すため非推奨化された。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

OAuthPKCEOIDC認可CSRFOAuthPKCEOIDC
参考: 公式情報