0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【セキュリティ】state が壊れると OAuth が死ぬ

Last updated at Posted at 2025-11-17

はじめに

OAuth は便利で強力。でも実は、CSRF(Cross-Site Request Forgery)に極めて弱い構造 を持っています。

その弱点を補うために存在するのが state パラメータ
これが適切に実装されていないと、OAuth は簡単に乗っ取られてしまいます。

この記事では、

  • なぜ OAuth は CSRF に弱いのか
  • state の役割
  • 実際に起きる攻撃シナリオ
  • TryHackMe の “Contact Sync” 例での攻撃フロー
  • 防御策とベストプラクティス

を、シンプルかつ本質的に解説します。


1. OAuth は本質的に「CSRF と戦う仕組み」

OAuth 2.0 の Authorization Code Flow をざっくり言うと、

  1. クライアント → 認可サーバへリダイレクト
  2. ユーザーはログインして同意
  3. 認可サーバ → クライアントに authorization code を返す
  4. クライアントが code を使って access token を取得

という構造。

しかし問題点が一つ。

このリダイレクト処理は「ブラウザ上で起きる」。
ブラウザ操作=CSRF でいくらでも偽装できる。

攻撃者はユーザーのブラウザを利用して OAuth の一部を“代わりに実行させる”ことができるわけです。


2. state パラメータは「セッションの錨(いかり)」

CSRF を防ぐために OAuth が採用しているのが state パラメータ

  • クライアントがランダムな値を生成して認可リクエストに含める
  • 認可サーバはそれをレスポンスにも含めて返す
  • クライアントは 返ってきた state が一致するか確認 する

一致しなければ即拒否。

つまり state は、

「この OAuth は本当に俺が開始したやつ?」を確認するための署名

のような役割。

state がない → すべての OAuth リダイレクトが“誰のものか分からない状態”
=CSRF が即成立します。


3. state が欠落した場合に起こる攻撃

—— “Authorization Code Injection Attack”

攻撃者がやることはシンプル。

攻撃の狙い

被害者のアプリ上で“攻撃者の authorization code” を代わりに処理させる。

結果

被害者アプリが勝手に攻撃者の OAuth アカウントと連携される。
つまり アカウント乗っ取り or データ奪取


4. TryHackMe のシナリオで攻撃を再現

ここが面白いので、ストーリー形式で描きます。


Attacker の準備

攻撃者は自分の authorization code を入手します。

http://coffee.thm:8000/o/authorize/?response_type=code&client_id=xxx&redirect_uri=http://coffee.thm:8000/oauthdemo/callbackforcsrf/

この URL を踏むだけで code が取れてしまう“甘い実装”。

レスポンス例:

{
  "code": "xI86EEVxQ4LY8vfskl8yTQTq8Kp5dj",
  "Payload": "http://coffee.thm/csrf/callbackcsrf.php?code=xI86EEVxQ4LY8vfskl8yTQTq8Kp5dj"
}

得られた Payload(攻撃 URL) を被害者に踏ませるだけで攻撃成功。


Victim がリンクをクリックした瞬間…

被害者のブラウザは攻撃者の code を含んだ URL を開きます。

http://bistro.thm:8080/csrf/callbackcsrf.php?code=xI86EEVxQ4LY8vfskl8yTQTq8Kp5dj

次に起きること:

  1. 被害者セッションで OAuth token exchange が起こる
  2. アプリは 攻撃者の authorization codeaccess token を取得
  3. 結果、攻撃者アカウントが被害者アプリに接続される

つまり:

被害者が攻撃者のアカウントにデータをプレゼントしてしまう💀

今回の例では
連絡先(Contacts)が攻撃者へ丸ごと送られます。

そしてフラグ:

THM{CONTACTS_SYNCED}

5. なんでこんな簡単に攻撃成功するの?

理由はただ一つ。
state が無い
or
static / predictable な state
or
state を検証していない

OAuth でやってはいけない3大悪。

state が無ければ OAuth は CSRF 取り放題状態 になります。


6. 防御策(本番環境では必須)

state を必ず使う(ランダム・使い捨て・長い)

  • 暗号学的に強いランダム値
  • セッションに保存
  • 1回限り
  • 最低 128bit の強度

redirect_uri を厳密に固定

  • 完全一致(パラメータ自由度を与えない)
  • ワイルドカードは絶対禁止

PKCE を併用(スマホアプリなら必須)

CSRF ではなく Authorization Code Interception 対策だけど
攻撃表面をさらに削れる。

SameSite=Lax or Strict の Cookie 設定

OAuth ライブラリの標準実装を使うこと


まとめ

皮肉だけど、本質はこれ。

OAuth は「第三者アプリにデータを渡す仕組み」を提供する
→ 必然的に CSRF と構造が似てしまう
→ state を正しく使わないと 全部崩壊する

OAuth の実装でミスると:

  • アカウント乗っ取り
  • データ窃取
  • メール引き継ぎ
  • 決済連携の悪用
  • SNS 連携の強制紐付け
  • サプライチェーンの危険

想像以上に影響が大きい。

だからセキュアコーディングでは、

「state を使え」よりも「state が生命線」

と覚えたほうがいいです。


OAuth × CSRF 攻撃シーケンス図(攻撃者 → 被害者)

攻撃者が Authorization Code を取得し、被害者に踏ませる流れ

CSRF × OAuth 攻撃の「概念図」

(OAuth 全体構造の中でどこが乗っ取られているか視覚化)

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?