OIDCのstate/nonce/PKCEの違いを整理する
OpenID Connect (OIDC) を学習していると、state/nonce/PKCE という、よく似た役割を持つように見えるパラメータが登場します。
これらが混同されやすいのは、「リクエスト時に値を送り、レスポンス時にそれを検証する」という基本的な動きが同じだからです。しかし、それぞれが守る対象とタイミングは全く異なります。
この記事では、それぞれの役割と違いを整理し、「なぜ3つとも必要なのか?」という疑問を解消します。
OIDC の基本フロー
まず簡単に流れをおさらいしましょう。
- クライアント(フロントエンド)が認可リクエストを送る
- ユーザーが IdP(認可サーバー)で認証する
- 認可コードを受け取る
- クライアントがトークンエンドポイントにアクセスして
ID TokenやAccess Tokenを取得する
この間に「なりすまし」や「リプレイ攻撃(過去のリクエストを再利用)」など、さまざまなリスクがあります。
その防御に使われるのが state / nonce / PKCE です。
state
RECOMMENDED. リクエストとコールバックの間で維持されるランダムな値. 一般的に Cross-Site Request Forgery (CSRF, XSRF) 対策の目的で利用される, ブラウザ Cookie と紐づく暗号論的にセキュアな値を取る.
役割
state は「リクエストした本人であることを確認するための一時トークン」です。
認可リクエストを送るときに、クライアントがランダム文字列を生成して state パラメータに入れます。
そしてコールバック時に返ってきた state が一致していれば、「自分が送ったリクエストに対するレスポンスだ」とわかります。
これにより、悪意あるサイトがあなたのブラウザから勝手にリクエストを送っても、
state が一致しないため処理を拒否できます。
🚨もしstateがなかったら?
state は「このリクエストは、確かに自分が意図して始めたものだ」と確認するための目印です。
悪意のある攻撃者が用意した罠サイトに、SNSの「アカウント連携解除」のURLが仕込まれていたとします。あなたがそのSNSにログインしたまま罠サイトのリンクをクリックすると、意図せず連携が解除されてしまうかもしれません。これがCSRF攻撃です。
stateがあれば、クライアントは「自分が発行した覚えのないリクエストだ」と判断して処理を拒否できるため、このような攻撃を防ぐことができます。
イメージ図
nonce
Client セッションと ID Token を紐づける文字列値. リプレイアタック防止のために用いられる. Authentication Request で指定されたままの値を ID Token に含める. ID Token に nonce が含まれる場合, Client は Authentication Request に含めた nonce 値が ID Token に含まれる nonce Claim Value と一致することを検証しなければならない (MUST). Authentication Request に nonce が含まれていた場合, Authorization Server は ID Token に Authentication Request で受け取ったそのままの Claim Value で nonce Claim を含めねばならない (MUST). Authorization Server は, 受け取った nonce に対して上記以外のなんらの処理も行うべきではない (SHOULD). nonce は大文字小文字を区別する文字列である.
役割
nonce は「一度限りのトークン(number used once)」です。
OIDC では ID Token(JWT)内に nonce クレームが含まれます。
クライアントは認可リクエスト時に nonce を指定し、
返ってきた ID Token の中の nonce 値が一致するか検証します。
もし攻撃者が過去の ID Token を使ってログインを偽装しようとしても、
その nonce はすでに使われているため拒否されます。
nonce は「このIDトークンは、今回の一連の認証フローで発行された、新品のトークンである」ことを保証します。
🚨もしnonceがなかったら?
攻撃者が過去の通信で傍受したあなたのIDトークンを再利用して、あなたになりすましてサービスにログインしようとするかもしれません。これがリプレイ攻撃です。
nonceを検証することで、クライアントは「このIDトークンは以前使われたものではない」と確認でき、なりすましを防ぎます。
stateとの違い
- state: 認可リクエストからコールバックまでの一連のフローが正当であることを保証する。
- nonce: 受け取ったIDトークンそのものが使い回されていないことを保証する。
流れ
PKCE
役割
PKCE (Proof Key for Code Exchange) は、主にclient_secretを安全に保持できないモバイルアプリやSPAで、認可コードの横取りを防ぐための仕組みです。この攻撃は「認可コード傍受攻撃 (Authorization Code Interception Attack)」と呼ばれます。
code_verifierとcode_challengeの関係は、パスワードとそのハッシュ値の関係によく似ています。
クライアントは「パスワード」にあたるcode_verifierを生成します。
それをハッシュ化したcode_challengeを、先に認可サーバーへ送ります(「こういうパスワードで後からリクエストするからね」と予告するイメージ)。
トークンを取得する際に、答えであるcode_verifierを送信します。
これにより、たとえ通信経路の途中で認可コードが盗まれても、code_verifier(元のパスワード)を知らない攻撃者はトークンを取得できません。
流れ
- クライアントがランダム文字列
code_verifierを生成 - そのハッシュ(SHA256)を
code_challengeとして認可リクエストに送信 - 認可コード取得後、トークンリクエストで
code_verifierを送信 - サーバー側で検証し、一致すればトークン発行
こうすることで、たとえ攻撃者が認可コードを盗んでも、code_verifier がなければトークンを取得できません。
まとめ
OIDC の state, nonce, PKCE はいずれも「攻撃を防ぐための防壁」ですが、それぞれ守備範囲が異なります。
| 項目 | 目的 | 使われる場面 | 防ぐ攻撃 | チェック方法 |
|---|---|---|---|---|
| state | 認可リクエストの正当性を保証 | 認可リクエスト → コールバック | CSRF攻撃 | リクエスト時とコールバック時の値が一致するか検証 |
| nonce | IDトークンの使い回しを防止 | IDトークン受け取り・検証時 | リプレイ攻撃 | IDトークン内のnonceクレームがリクエスト時の値と一致するか検証 |
| PKCE | 認可コードの横取りを防止 | 認可コード → トークン交換 | 認可コード傍受攻撃 | トークンリクエスト時のcode_verifierから生成したハッシュ値が、認可リクエスト時のcode_challengeと一致するか検証 |
この3つを正しく実装することで、OIDCの認証フローは堅牢で安全になります。
Discussion