SameSite Cookieとクロスサイト送信の制御モデル
CSRFの主因であるCookieの自動送信を、SameSite属性で原理から制御。Strict/Lax/Noneの送信判定とトップレベル例外、CHIPSまで押さえ、防御の効果と限界を正確に設計できます。
- 1.送信可否は「リクエストのサイト=Cookieのサイトか」で決まる。Strict は常にクロスサイト送信を遮断、Lax はトップレベルナビゲーションのGETに限り許可、None は常に送るが Secure 必須。
- 2.Lax の例外はあくまで安全なメソッドのトップレベル遷移のみ。POST・iframe・fetch・画像読み込みはクロスサイトで送られないため、Lax でも CSRF の主要経路は塞がれる。
- 3.SameSite は CSRF を大幅に緩和するが万能ではない。サブドメインや同一サイト内の攻撃には無効で、トークン併用が必要。CHIPS は分離した Cookie をクロスサイト埋め込みでも安全に使うための別軸。
SameSite 属性は、ブラウザが Cookie を「どのリクエストに自動で付けるか」を制御する仕組みです。CSRF の根本原因は Cookie がクロスサイトのリクエストにも自動付与されることにありました。SameSite はその付与条件をサイト境界で絞り込み、攻撃経路を構造的に塞ぎます。判定アルゴリズムと例外、そして緩和策としての限界まで正確に押さえましょう。
送信判定は「サイト」の一致で決まる
SameSite の判定単位はオリジンではなくサイトです。ここでのサイトは schemeful same-site、すなわち scheme + eTLD+1(登録可能ドメイン)の組です。https://app.example.com と https://api.example.com はホストが違ってもサイトは同じ example.com なので、両者間の通信は同一サイトとして扱われます。
判定で比較されるのは2つのサイトです。
- Cookie のサイト: その Cookie を発行したサイト(登録ドメイン)。
- リクエストのコンテキスト: そのリクエストを生んだトップレベルドキュメントのサイト。
この2つが一致すれば same-site リクエスト、食い違えば cross-site リクエストです。重要なのは、判定の基準が「リクエスト先 URL のサイト」ではなく「そのリクエストを開始したページのサイト」だという点です。evil.example 上のスクリプトが bank.example へ送るリクエストは、宛先が bank でも開始元が evil なのでクロスサイトと判定されます。
現在の判定は scheme を含みます。http://example.com と https://example.com は別サイト扱いです。HTTP から HTTPS への混在環境では、same-site のつもりの遷移でも Cookie が落ちることがあります。
Strict / Lax / None の挙動
3つの値は、クロスサイトリクエストへの付与可否で段階的に異なります。
| 値 | same-site | cross-site | 備考 |
|---|---|---|---|
| Strict | 常に送る | 一切送らない | 外部リンク経由の初回遷移でも付かない |
| Lax | 常に送る | トップレベルナビゲーションの安全なメソッドのみ送る | 属性無指定時の既定値 |
| None | 常に送る | 常に送る | Secure 必須。無ければブラウザが拒否 |
Strict はクロスサイト発のリクエストには例外なく Cookie を付けません。最も堅牢ですが、他サイトのリンクから自サイトへ飛んだ初回読み込みでもセッション Cookie が送られず、「リンクを踏んだのに未ログイン表示」になる UX 問題を招きます。
None は従来の「常に送る」挙動で、クロスサイト埋め込み(広告・SNS ウィジェット・サードパーティ API)に必要です。ただし Secure 属性が必須で、HTTPS 上でしか送られません。
属性を書かなかった場合の既定は Lax です(主要ブラウザ)。明示しなくても最低限の CSRF 耐性が働く設計になっています。
Lax のトップレベルナビゲーション例外
Lax の核心は、クロスサイトでも例外的に Cookie を送る条件を厳密に定義している点です。両方を満たすときだけ送られます。
- トップレベルナビゲーションであること — アドレスバーの URL が変わる遷移。
<a>クリック、window.location代入、フォーム送信による画面遷移が該当する。iframe 読み込み、fetch/XHR、<img>・<script>・<link>のサブリソース取得は該当しない。 - 安全な(状態を変えない)HTTP メソッドであること —
GET・HEAD・OPTIONSのみ。POST・PUT・DELETEは対象外(PUT/DELETEは冪等だが「安全」ではないため送られない)。
この2条件により、攻撃者が最も使いたい経路はすべて塞がれます。
| クロスサイトからの操作 | Lax で Cookie は付くか |
|---|---|
| 罠ページのリンクを踏んで遷移(GET) | ✅ 付く(トップレベル+安全メソッド) |
| 罠ページの自動送信フォーム(POST) | ❌ 付かない(メソッドが対象外) |
| 隠し img の src で GET リクエスト | ❌ 付かない(サブリソースでトップレベルでない) |
| fetch / XHR でのバックグラウンド送信 | ❌ 付かない(ナビゲーションでない) |
CSRF の典型攻撃は「自動送信される隠しフォーム POST」や「画像タグによる GET 副作用」です。前者はメソッド条件で、後者はトップレベル条件で弾かれます。つまり Lax でも、状態を変える操作(POST 等)への CSRF はほぼ防げます。残る穴は「GET で状態を変える設計」で、これは Lax の例外に乗ってしまうため、副作用のある操作を GET にしないこと自体が前提です。
GET /logout や GET /transfer?to=... のように GET で状態を変えると、クロスサイトのトップレベル遷移で Cookie が付き、CSRF が成立します。状態変更は必ず POST 等の非安全メソッドに割り当て、GET は読み取り専用に保ってください。
Secure と Partitioned(CHIPS)
SameSite と組み合わせて使う2つの属性が、送信の安全性とスコープをさらに制御します。
Secure は HTTPS 接続でのみ Cookie を送る指定です。SameSite=None には必須で、欠けるとブラウザが Cookie を破棄します。平文経路での漏えいを防ぐ前提条件と考えてください。
Partitioned は CHIPS(Cookies Having Independent Partitioned State) を有効化する新しい属性です。背景にはサードパーティ Cookie のトラッキング問題があります。従来の SameSite=None Cookie は、どの埋め込み先からでも同じ値が共有されるため、サイトをまたいだ追跡に悪用されました。CHIPS はこの共有を断ち切ります。
| 項目 | 通常の SameSite=None | Partitioned(CHIPS) |
|---|---|---|
| 保存キー | Cookie 自身のサイトのみ | (トップレベルサイト, Cookie のサイト)の組 |
| クロスサイト追跡 | 可能(同一値が全埋め込み先で共有) | 不可(埋め込み先ごとに別パーティション) |
| 用途 | 汎用のサードパーティ Cookie | 埋め込みウィジェットの状態保持など正当な用途 |
CHIPS では Cookie が「トップレベルサイトごとのパーティション」に隔離されます。shop-a.example に埋め込まれた widget.thirdparty.example の Cookie と、shop-b.example に埋め込まれた同じウィジェットの Cookie は別物になり、横断追跡できません。チャット部品やマップ埋め込みのように「埋め込み先ごとに独立した状態」を持てれば十分な用途を、トラッキングを許さずに成立させるための仕組みです。Partitioned は Secure かつ SameSite=None と併用するのが基本形です。
HTTP/1.1 200 OK
Set-Cookie: sid=abc123; HttpOnly; Secure; SameSite=Lax; Path=/
Set-Cookie: widget_state=on; Secure; SameSite=None; Partitioned; Path=/
CSRF 緩和としての効果と限界
SameSite=Lax(既定)や Strict は、クロスサイト発の状態変更リクエストから Cookie を外すことで、CSRF を構造的に緩和します。トークン方式が「正しいトークンを持つか」を検証するのに対し、SameSite は「そもそも Cookie を送らない」ため、サーバー側の検証ロジックすら不要なのが強みです。ただし境界条件に弱点が残ります。
| 弱点・限界 | なぜ防げないか |
|---|---|
| サブドメイン間の攻撃 | サブドメインは同一サイト(same-site)扱いのため、XSS 等で奪取した同一サイト上のページからは Cookie が普通に付く |
| 同一サイト内の CSRF / XSS 起点 | 攻撃の起点が same-site なら SameSite 判定は素通り。XSS の根本対策にはならない |
| GET による副作用操作 | Lax の例外でクロスサイトでも送られるため、設計次第で CSRF が残る |
| 古い・非対応クライアント | SameSite を解釈しない環境では既定の防御が効かない |
要するに SameSite は「サイトをまたぐ自動送信」を断つ防御であり、サイト境界の内側で起きる攻撃には無力です。実務では、重要操作に CSRF トークン(または Origin/Sec-Fetch-Site ヘッダ検証)を併用する多層防御が定石です。Sec-Fetch-Site: cross-site をサーバーで弾けば、SameSite と独立した第2の関門になります。
セッション Cookie は HttpOnly + Secure + SameSite=Lax を基本とし、状態変更は POST 等に限定。さらに重要操作には CSRF トークンか Sec-Fetch-Site 検証を重ねる。これでクロスサイト・盗聴・XSS 経由の3方向をまとめて下げられます。
Cookie とセッションの土台は Cookie とセッション、CSRF と並ぶ XSS 側の防御は CSP(コンテンツセキュリティポリシー)の内部、別オリジン通信での資格情報の扱いは CORS(オリジン間リソース共有) もあわせてどうぞ。
Web/フロントエンド Article
SameSite Cookieとクロスサイト送信の制御モデルを実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
セキュリティ
比較で見る軸
難易度: advanced / カテゴリ: Web/フロントエンド / タグ数: 5
導入後に効く点
Lax の例外はあくまで安全なメソッドのトップレベル遷移のみ。POST・iframe・fetch・画像読み込みはクロスサイトで送られないため、Lax でも CSRF の主要経路は塞がれる。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- Web/フロントエンド
- タグ数
- 5
判断チェックリスト
- 自社の用途が「セキュリティ / Cookie」に近いか確認する。
- 強みである「送信可否は「リクエストのサイト=Cookieのサイトか」で決まる。Strict は常にクロスサイト送信を遮断、Lax はトップレベルナビゲーションのGETに限り許可、None は常に送るが Secure 必須。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。