8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

攻撃シナリオで理解する OAuth 2.1 ― OAuth 2.0 のどこが危ないのか

8
Last updated at Posted at 2025-12-01

はじめに

最近twitterでOAuth2.1なるものの話を聞いたが、デファクトのOAuth2.0以外全然知らなかったので備忘録として簡単にまとめておく。結論から言うとOAuth 2.1 はOAuth 2.0 を捨てて作り直した新仕様ではなく、後付けのベストプラクティスを統合して整理した仕様。ちなみに執筆時点(2025-12)でInternet-Draftなので特に効力があるものでもない。

より具体には、次のものをまとめて整理した統合版である。

  • OAuth 2.0 本体: RFC 6749 / RFC 6750
  • PKCE: RFC 7636
  • ネイティブアプリ向け: RFC 8252
  • OAuth 2.0 Security Best Current Practice: RFC 9700(旧 draft-ietf-oauth-security-topics)

その過程で、実運用上危険・非推奨だったフローやパラメータの使い方が仕様レベルで削られている。この記事では、変更点にだけフォーカスして、フローで視覚化する。

OAuth 2.0 → 2.1 の主な変更点

OAuth 2.1 は、OAuth 2.0 と後付けベストプラクティスを統合して、危険な使い方を仕様レベルで潰した整理版である。
ここでは、変更点ごとに

  • 何が変わるのか
  • フロー(mermaid)
  • なぜ危険だったのか(どう攻撃されるのか)
    をまとめる。

1. Authorization Code + PKCE がデフォルトフローになる

何が変わるか

  • Authorization Code フローでは PKCE 利用が前提になる
  • 特に Public クライアント(SPA、ネイティブアプリ)では必須
  • サーバサイド Web アプリでも PKCE 利用が推奨される

フロー(Authorization Code + PKCE)

なぜ PKCE なしが危険か

PKCE (ピクシーって呼び方可愛いよね)無しの Authorization Code フローでは、認可コードの奪取がそのままアクセストークン奪取につながる。

代表的なパターン:

  • 攻撃者がブラウザやネットワークを制御して、code を横取り
  • 横取りした code を使って、自分の環境から /token を叩く
  • 正規クライアントになりすましてアクセストークンを取得

PKCE があると:

  • 認可コード発行時に code_challenge を AS へ登録
  • トークン発行時に code_verifier から計算した値と突き合わせ
  • クライアント自身しか知らない code_verifier がないとトークンに交換できない

つまり、コードを盗られてもトークンにはできない状態になる。

2. redirect_uri は事前登録値と完全一致で検証

何が変わるか

  • redirect_uri は AS に事前登録した値と文字列完全一致で検証する
  • プレフィックス一致やワイルドカード(https://example.com/*)は NG
  • 認可リクエストとトークンリクエストの両方で同じ URI を確認する

フロー(完全一致チェック)

なぜ緩いマッチが危険か(攻撃例)

もし AS が

  • https://app.example.com/ で始まれば OK」
  • https://app.example.com/callback* を全部許可」

のようなチェックをしていると、攻撃者は:

  1. 攻撃者が管理する URL を https://app.example.com/callback.evil.com のように作る
  2. その URL を redirect_uri として指定してユーザを認可画面に誘導
  3. 認可完了後、AS が攻撃者サイトへ認可コードをリダイレクト
  4. 攻撃者がコードを使ってアクセストークンを取得

という形で、オープンリダイレクトを利用したトークン/コード窃取が成立する。ASがガバ実装してる時など
完全一致を強制することで、こうした紛らわしいURIを封じることが狙い。ログインページ前にいたところに戻す等の実装は/auth/callbackに戻ってきた後で、アプリは好きにリダイレクトしてよい。

3. Implicit Grant(response_type=token)削除

何が変わるか

  • response_type=token の Implicit フローは仕様から削除される
  • SPA でも Authorization Code + PKCE を使うことが前提になる
  • アクセストークンを URL フラグメントで直接受け取る設計をやめる

フロー(旧 Implicit を拒否するイメージ)

なぜ Implicit が危険か(攻撃・脆弱性の観点)

Implicit では、アクセストークンがブラウザの URL フラグメント(#access_token=...)に露出する。この設計が現代ブラウザ環境では危険になっている。

代表的なリスク:

  • フラグメントを JS でパースする実装が雑だと、別オリジンのスクリプトにトークンが漏れる
  • ブラウザ拡張・プラグイン・ログ機構などが URL 全体を収集していると、そこでトークンが収集される
  • ヒストリ、スクリーンショットなどで人間経由の漏洩も起こりやすい

また、Implicit には PKCE のようなトークン発行前の追加検証の仕組みがなく、
アクセストークンがその場で発行されるため、途中で何かがすり替わっていないかを確認する手段を持ちにくい。

Code + PKCE に統一することで:

  • トークン発行は常にトークンエンドポイント経由
  • コードと PKCE を使って盗難・リプレイを防ぐ
  • ブラウザ URL にアクセストークンを出さない

という整理になる。

4. Resource Owner Password Credentials(ROPC)削除

何が変わるか

  • grant_type=password を使うフローは仕様から削除される
  • クライアントはユーザの ID / PW を直接扱わず、AS に認証を任せるのが前提になる

フロー(旧 ROPC を拒否するイメージ)

なぜ ROPC が危険か(攻撃・運用上の問題)

ROPC の最大の問題は何と言ってもクライアントがユーザの ID/PW を知ってしまう点である。

  • クライアントアプリが本当に信頼できるかどうかは環境次第

    • モバイルアプリなら改ざん・リバースエンジニアリングし放題
    • デスクトップアプリならマルウェアに覗かれても気づきにくい
  • ユーザは同じ ID/PW を他サービスでも使い回していることが多い

    • 1 つのクライアントが漏洩すると、他サービスへの攻撃にも利用される

また、セキュリティ UX 的にも悪い:

  • Phishing サイトが「正規クライアント」を装って ID/PW を集めやすい
  • ユーザはどこにパスワードを入れてよくて、どこはダメなのか区別できなくなる

Authorization Code フロー + AS でのログイン画面に統一することで、

  • パスワードを入れる場所は AS のログイン画面だけ
  • クライアントはトークンだけを扱う

という境界が明確になり、設計・攻撃両面でマシになる。

5. Bearer トークンを URL クエリに載せるのを禁止

何が変わるか

  • https://api.example.com/resource?access_token=... のような URI クエリへのトークン埋め込みは禁止
  • Authorization: Bearer ... などヘッダやボディで送る

フロー(クエリを拒否してヘッダに統一)

なぜクエリが危険か(漏洩パス)

URI クエリは、システムのあちこちに残りやすい。

  • Web サーバのアクセスログ
  • リバースプロキシ、API Gateway のログ
  • ブラウザのアドレスバー、履歴、オートコンプリート
  • リファラヘッダ(別サイトにアクセスした際に送られることがある)
  • APM / モニタリングツールが収集する URI

これら全てがトークンが雑にばら撒かれる経路になる。
ヘッダに載せる場合でもログに残りうるが、少なくともURI全文を集める仕組みに自動で吸い込まれるのは避けられる。

6. リフレッシュトークンはローテーション等で保護が前提

何が変わるか

  • リフレッシュトークンは、「そのまま長寿命トークンとして使い回す」前提が潰される

  • 基本戦略は以下いずれか:

    • リフレッシュトークンを 1 回使うたびに新しいものを発行し、古いものを失効(ローテーション)
    • mTLS / DPoP などで「特定クライアントにだけ有効」な sender-constrained token にする

フロー(リフレッシュトークンのローテーション)

なぜ固定リフレッシュトークンが危険か(攻撃シナリオ)

固定の長寿命リフレッシュトークンが一度漏洩すると:

  • 攻撃者は半永久的に新しいアクセストークンを取得し続けられる
  • アクセストークンの短寿命化(例: 5 分、有効期限)にほとんど意味がなくなる
  • トークンの利用ログから「どれが正規利用でどれが攻撃か」を見分けにくい

ローテーションを行うと:

  • 1 回使うごとに旧トークンは無効化される
  • 正規クライアントと攻撃者が同時にトークンを使うと、「2 回目以降」が必ずエラーになり、不正利用の兆候として検知できる
  • 漏洩しても被害時間が限定される

sender-constrained(mTLS / DPoP)を組み合わせれば、「盗んだトークンを別の環境から使う」攻撃も防げる。

まとめ

OAuth 2.1 の変更点は、攻撃例を見ての通り、危ない運用パターンを仕様から追い出すことに集中している。今の時代で見るとそりゃマズいだろって実装だけど、OAuth 2.0ってもう15年ぐらい前の奴だし多少はね?

実装側は、ここで挙げたフローと攻撃パターンを踏まえて、自分の環境がどこまで 2.1 的な前提に寄っているかを確認すればよいかと思う。

これよりさらに上位版のFAPI2.0 (Financial Grade API)というものもあるので改めてまとめたい。

8
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?