CSRF対策としてSameSite Cookieを利用しているサイトは多いですが、実装によっては防御を簡単に回避されるケースがあります。
この記事では、Web Security Academyのラボ「SameSite Lax bypass via method override」をもとに、
SameSite=Laxがどのように機能するのか- なぜGETリクエストで攻撃が成立するのか
- WordPressで起こり得る実装ミス
を整理します。
CSRFの基本的な仕組みがあいまいな場合は、先にこちらを読むと理解しやすくなります。
SameSite Lax bypass via method overrideラボの概要
対象機能はメールアドレス変更です。
- CSRFトークンなし
- セッションCookieはSameSite未設定(=Lax扱い)
- エンドポイントはPOSTのみ受け付け
一見すると、SameSiteによりCSRFは防げているように見えます。
攻撃の流れ|GETリクエストでCSRFが成立するまで
攻撃の流れは以下の通りです。
- 被害者がログイン状態のブラウザを持っている
- 攻撃者が用意したページを開く
- そのページからGETリクエストが発生する
SameSite=LaxによりCookieが送信される- サーバーがそのリクエストをPOSTとして処理する
CSRFの成立理由
ポイントはブラウザとサーバーの認識のズレです。
ブラウザ側
- GETリクエストとして送信
- トップレベルナビゲーション → Cookieを付与(Laxの条件を満たす)
サーバー側
_method=POSTを解釈 → POSTリクエストとして処理
結果
- ブラウザ:GETだから安全と判断
- サーバー:POSTとして処理
CSRF成立
「トークンが存在しない」「検証が甘い」といった問題と組み合わさると、さらに深刻になります。
▶ WordPressで考えるCSRF対策の落とし穴|トークン未送信で突破されるケース
なぜSameSite=Laxでも防げないのか
SameSite=LaxはCSRF対策として一定の効果がありますが、完全ではありません。
理由は、トップレベル遷移ではCookieが送信されるという仕様にあります。
SameSite=Laxの挙動
SameSite=Laxでは、次のような場合にCookieが送信されます。
- ユーザーがリンクをクリックしたとき
- アドレスバー遷移などの通常のページ移動(GETリクエスト)
一方で、以下のようなケースではCookieは送信されません。
- フォームのPOST送信
- iframeや画像などのサブリクエスト
このラボで起きていること
このラボでは、本来POSTで処理されるべき機能に対して、
GET + _method=POSTという形でリクエストを送ることができます。
つまり、
- リクエストとしてはGET(ブラウザはCookieを送る)
- サーバー側ではPOSTとして処理される
というズレが発生しています。
なぜ攻撃が成立するのか
攻撃者は、次のようなスクリプトを用意します。
<script>
document.location = "https://example.com/change-email?email=hacker@example.com&_method=POST";
</script>これを被害者が開くと、
- ページ遷移(トップレベルのGET)が発生する
SameSite=LaxによりCookieが送信される- サーバーはPOSTとして処理する
- ログイン済みユーザーとしてメールアドレスが変更される
という流れになります。
まとめ
SameSite=Laxが防げるのは、自動送信されるPOSTリクエストです。
しかし、ユーザー操作を伴うGETリクエストは防げません。
このように、ブラウザの挙動とサーバー側の実装にズレがあると、SameSiteに依存した対策は簡単に回避されてしまいます。
攻撃者視点でどう見えるか
この機能を見たとき、攻撃者はこう考えます。
- CSRFトークンがない → 攻撃できそう
SameSite=Lax→ POSTは無理だがGETなら可能- GETは使えない → ではPOSTとして処理させる方法はないか?
ここで「method override」に気づけば突破できます。
実際の攻撃は、こうした複数の弱点を組み合わせて成立します。
▶ WordPressで考えるCSRFトークンの欠陥│セッションと紐づいてないトークンはなぜ危険なのか
WordPressで起こり得る実装ミス
WordPressでも同様の構造は普通に発生します。
① REST APIでのメソッド上書き
一部のプラグインやカスタム実装では、
_methodX-HTTP-Method-Override
などを使ってHTTPメソッドを変更できるようにしている場合があります。
② GETで状態変更が可能な処理
例
- メール変更
- 設定更新
- 注文処理
GETで処理できる構造は、SameSite=Laxと組み合わさると危険です。
③ nonce未検証
WordPressの標準対策はnonceですが、
- nonce未実装
- nonceがすべての経路で検証されていない
といったケースではCSRFが成立します。
セキュリティ診断でのチェックポイント
以下を確認することで、この種の問題は見つかります。
① GETで状態変更できないか
URLパラメータで状態が変わるかを確認
② メソッド上書きが使えないか
_methodX-HTTP-Method-Override
を付与して挙動を確認
③ SameSiteに依存していないか
SameSiteだけで防御している設計は危険
④ nonceが正しく検証されているか
すべての更新処理で検証されているか確認
対策|SameSiteに依存しないCSRF防御
① CSRFトークン(nonce)を必ず検証する
SameSiteは補助的な対策に過ぎません。
② GETで状態変更を行わない
- GET:取得のみ
- POST:更新処理
この原則を守ることが重要です。
③ メソッド上書きを安易に許可しない
不要な場合は無効化します。
④ SameSiteに依存しない設計
SameSiteは防御の一部であり、単体では不十分です。
まとめ|SameSiteだけでは不十分な理由
SameSite=LaxはCSRF対策として有効ですが、完全ではありません。
特に以下の条件が揃うと簡単に突破されます。
- CSRFトークンがない
- GETリクエストが許可されている
- メソッド上書きが可能
「ブラウザはGETとして扱うが、サーバーはPOSTとして処理する」というズレが、攻撃の成立条件になります。
CSRF対策は単一の方法では不十分で、実装全体を通して考える必要があります。
関連記事もあわせて確認してください。
▶ CSRFの基本と仕組み
▶ WordPressで考えるCSRF対策の落とし穴|トークン未送信で突破されるケース
▶ WordPressで考えるCSRFトークンの欠陥│セッションと紐づいてないトークンはなぜ危険なのか
▶ WordPressで考えるCSRF対策の落とし穴|POSTリクエストのみの検証では防げない理由
