0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【セキュリティ】SameSite Cookie Bypass

0
Posted at

はじめに

SameSite Cookie は、CSRF 対策として広く採用されている
ブラウザ主導の防御メカニズムです。

多くの開発者はこう考えます。

「SameSite を設定しているから CSRF は大丈夫」

しかし実際のペンテスト現場では、
SameSite を正しく理解していない実装が、
そのまま 攻撃ベクトルになることが珍しくありません。

本記事では、

  • SameSite の基本設計
  • SameSite=Lax を悪用した CSRF(GET)
  • Lax + POST を成立させる Chrome の仕様例外
  • ペンテスター視点での本質的な問題点

を、実攻撃シナリオに沿って解説します。


SameSite Cookie とは何か(要点整理)

SameSite 属性は、Cookie に付与される制御フラグで、

「この Cookie、クロスサイトリクエストでも送る?」

という判断を ブラウザに任せるための仕組みです。


SameSite の 3 つのモード

SameSite=Strict

  • Cookie は 完全に同一サイトのみ
  • クロスサイト経由では 一切送信されない

✔ CSRF 耐性:最強
✖ UX:外部リンクでログイン切れが発生しやすい


SameSite=Lax

  • 原則クロスサイトでは送信されない
  • ただし以下は 例外的に送信される
    • トップレベルナビゲーション
    • GET / HEAD / OPTIONS

✔ UX とセキュリティの妥協点
⚠️ 「部分的に送られる」ことが最大の罠


SameSite=None

  • すべてのクロスサイトリクエストで送信
  • Secure(HTTPS)必須

✔ iframe / SSO 用
✖ CSRF 防御としては期待不可


SameSite=Lax を使った CSRF 攻撃(GET)

攻撃シナリオ概要

  • 被害者 Josh は銀行サイト mybank.thm にログイン中
  • ログイン時に logout Cookie(SameSite=Lax) が発行される
  • サーバーは Cookie の値のみでログアウト処理を実行
if ($_COOKIE["logout"] == "xxxxxxx") {
    session_destroy();
}

攻撃者の目的

Josh を強制ログアウトさせる


攻撃方法

攻撃者は Josh に以下のようなメールを送信します。

<a href="https://mybank.thm:8080/logout.php" target="_blank">
Survey Link!
</a>

なぜ成立するのか?

  • <a> クリック = トップレベルナビゲーション
  • HTTP メソッド = GET
  • SameSite=Lax → Cookie が送信される

結果:

  • logout Cookie が送信される
  • サーバーは正規操作と誤認
  • Josh は即ログアウト

何が問題だったのか?

設計上のミス
  • 状態変更(logout)を GET で実装
  • SameSite=Lax を「安全」と誤解
正しい設計なら?
  • logout は POST
  • CSRF トークン必須
  • 重要 Cookie は SameSite=Strict

「Lax なら POST は安全」は本当か?

答えは NO です。


Chrome の SameSite 例外仕様(2 分ルール)

Google Chrome の公式仕様より要約すると:

SameSite 属性が 指定されていない Cookie は、
設定・更新から 2 分以内であれば、
POST を含むクロスサイトリクエストでも送信される。

つまり:

  • Cookie 更新直後
  • 一時的に SameSite=None のように振る舞う

Lax + POST を成立させる攻撃チェーン

攻撃対象

  • isBanned Cookie
  • 値によってユーザー状態を制御

サーバー側コード(抜粋)

if (!isset($_COOKIE['isBanned'])) exit();

if (isset($_POST['isBanned'])) {
    document.cookie="isBanned=".$_POST['isBanned'];
}

開発者の意図

  • Cookie が無い POST を拒否
  • CSRF 対策のつもり

単純な POST が失敗する理由

  • クロスサイト POST
  • SameSite=Lax
  • Cookie が送信されない

👉 攻撃失敗 ❌


攻撃者の発想転換(重要)

  • isBanned Cookie は logout 時に更新
  • 更新後 2 分間は POST でも送信される

完成した攻撃チェーン

  1. 被害者を logout に誘導
  2. Cookie が更新される
  3. 2 分以内に POST を実行
  4. Cookie が送信される
  5. isBanned=true に変更成功

実際の攻撃コード例

<script>
function launchAttackSuccess(){
  let win = window.open("http://mybank.thm:8080/logout.php",'');
  setTimeout(function(){
    win.close();
    bank.submit();
  },1000);
}
</script>

<form name="bank" method="post"
action="http://mybank.thm:8080/index.php" style="display:none">
  <input name="isBanned" value="true">
</form>

ペンテスター視点の教訓

  • SameSite=Lax は 防御ではなく制限
  • Cookie 更新タイミングは攻撃面
  • Cookie を 認可・状態管理に使う設計自体が危険

防御側のベストプラクティス

  • 状態変更は POST + CSRF トークン
  • セッション Cookie は SameSite=Strict
  • Cookie をロジック判断に使わない
  • クライアント JS で Cookie を書き換えない

まとめ

SameSite は優秀ですが、万能ではありません。

SameSite は「盾」ではなく「条件付きルール」

特に Lax は最も誤解されやすい設定です。

CSRF を本気で防ぐなら、

SameSite + CSRF Token + 正しい設計

これが揃って、初めて「安全」と言えます。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?