OAuth PKCEはauthorization codeをトークンへ交換する際、要求を開始したクライアントが持つverifierも証明させる。codeだけを傍受した攻撃者は、正しいverifierなしに交換できないように設計される。これはcode交換の保護であり、すべてのトークン盗難や権限設定を解決する機能ではない。

要求ごとに異なる秘密を用意する

RFC 7636はauthorization要求ごとに高いエントロピーのcode_verifierを作ると定義する。指定された文字集合による43〜128文字が構文の範囲だ。長さが適切でも予測可能な値は暗号学的な乱数と同じではない。生成した値を該当要求に結び付け、交換段階まで保持する必要がある。

全ログインで同じ値を再利用すると要求ごとの結合が弱まる。複数の画面で開始する場合も、共通の保存欄を上書きして別の要求のcodeと組み合わせることがある。乱数生成だけでなく保存した値と要求の対応も確認する。この保存の点検は、規格を実装へ適用する実務的な提案である。

authorization要求にはchallengeを送る

S256ではverifierのASCII表現にSHA-256を適用し、その結果をbase64urlで表してcode_challengeにする。authorization要求にはchallengeと方式名を送る。元のverifierは後のトークン要求に使うため、この段階でそのまま公開しない。似た名前でも二つの値の役割は異なる。 パディングなしのbase64urlで表す。 S256変換の規定

RFCはS256を使えるクライアントにその利用を要求する。通常のbase64とbase64urlの混同や文字エンコードの誤りは、サーバー側の計算との不一致を起こす。規格の入力と出力例で変換を試せば、対話的なログイン全体を繰り返さずに変換部分の問題を切り分けられる。

トークンサーバーが結合を確認する

サーバーはchallengeをcodeに関連付け、交換時に受け取ったverifierを記録した方式で変換して比較する。RFC 7636は不一致をinvalid_grantで拒否すると定める。クライアントがchallengeを送っただけでは保護は完成せず、交換側の検証が必要だ。 一致は残りの検査へ進む条件であり、トークン発行の保証ではない。 トークン検証の規定

RFC 9700はpublic clientにPKCEの使用をMUSTとし、confidential clientにはRECOMMENDEDとしている。stateは応答を開始したアプリケーションのセッションに結び付ける。OpenID Connectのnonceは認証要求とID Tokenを結び、返されたclaimを検証する。認可サーバーがPKCEに対応すると確認できる場合に限り、PKCEをCSRF対策として信頼する。それ以外ではユーザーエージェントに安全に結び付けた一回限りのstateを使う。認証要求にchallengeがなかったのにtoken要求でverifierが送られた場合、サーバーはdowngrade防止のため拒否する必要がある。これはS256失敗後にplainへfallbackする問題とは別だ。

次は実行結果ではなく、拒否動作のテスト案です。verifierの欠落・不一致、別要求のverifier、challengeを省略した後のverifier送信、S256失敗後のplain再試行を確認します。正常な交換も試し、実行前に合格と記録しないでください。ログにはcodeやverifierの原文を残さず、秘密を含まない要求識別子と、成否・検証した結び付きだけを記録します。

失敗時に方式を弱めない

RFCはS256を試した後でplainへ下げることを禁止する。不一致なら、要求別の保存、エンコード、方式名、サーバーに対応付けたchallengeを先に調べる。保護を弱めてログインを成功させても、正しい交換を確認したことにはならない。

正常なcode交換と誤ったverifierの拒否を両方確かめることが導入確認の基準になる。同時ログインでも値が混ざらないかを確認する。開始要求とcode交換を結ぶ一つの統制として理解すると、ログイン全体を保証したと考えず、残る確認範囲を明確にできる。

出典

  • RFC 7636:verifierとサーバー検証。
  • OWASP OAuth2案内:要求結合と保護範囲。
  • RFC 9700:クライアント要件、条件付きCSRF対策、downgrade防止。