Dev Study
← Webセキュリティの実務
レッスン 4 / 12·約 8 分·第19期 Webセキュリティの実務

SameSite の判定単位は site — 兄弟サブドメインは同じ側にいる

リクエストがアプリケーションに届くまで
  1. ブラウザURL入力今ここ
  2. DNS名前→IP
  3. ルータ経路選択
  4. ロードバランサ振り分け
  5. Nginx受け口
  6. アプリ処理
  7. DB保存

前回は、フォームが送れた範囲の要求は確かめずに送られることを見ました。今回は、そうした要求に Cookie を付けるかどうかの判定単位を見ます。

まず結論

SameSite が比べるのは origin ではなく site(scheme + registrable domain)です。`https://a.example.test` と `https://b.example.test` は origin が違っても同じ site なので、手元の実測では3エンジンとも、兄弟から来た POST に `SameSite=Strict` の Cookie が付きました。

このコースの芯は「ブラウザが確かめられるのは『どこから来たか』まで、『本人が望んだか』はサーバが自分で確かめるしかない」でした。SameSite はその『どこから』を site の粒さで見る仕組みで、属性を書かなかった Cookie の扱いはエンジンごとに違います。

? 読む前に予測

b.example.test が SameSite=None / Lax / Strict / 属性なし の4つの Cookie を発行しました。そのあと a.example.test のページのフォームから b.example.test へ POST すると(どちらも https・同じポート)、headless Chromium ではどの Cookie が付くでしょうか。

当てることが目的ではありません。外した予測ほど記憶に残るので、直感で選んでください。

抽象原理

SameSite の Same は、origin ではなく site

SameSite は名前のとおり「同じ site か」を見ます。6265bis は同じ site かどうかを自分では定義せず、HTML Standard に任せています。

Two origins are same-site if they satisfy the "same site" criteria defined in [SAMESITE].

参照先の HTML では、site は origin から (origin's scheme, origin's host's registrable domain) を取り出したものです。比べるのは scheme と registrable domain だけで、ポートとサブドメインは比較に入りません。ブラウザの仕組み『分離の単位は origin ではなく site』で見た scheme + eTLD+1 と同じものです。

registrable domain は public suffix に左のラベルを1つ足したもので、どこまでが public suffix かは Public Suffix List(PSL)が決めます。どの規則にも当たらないときは既定規則 * により最右のラベルが public suffix になります。

今回の実験で使った .test は PSL に載っていません。したがって a.example.test と b.example.test の registrable domain はどちらも example.test で、同じ site です。一方 a.test と b.example.test は別の site です。

SameSite が比べているもの
https://a.example.test:8443要求を起こしたページの origin
https://b.example.test:8443Cookie の持ち主の origin
site を求める(サブドメインとポートを落とす)
(https, example.test)両方とも同じ site
same-site と判定
Strict の Cookie も付く誰が望んだ要求かは判定に入らない
  • ・origin は別でも site は同じ。SameSite の線は site の境目にしか引かれない
  • ・`.test` は PSL に無いので、既定規則 `*` で `test` が public suffix になる
具体実物

兄弟サブドメインから来た POST に、Strict が付く

自前サーバ(自己署名証明書、全ホスト名を 127.0.0.1 へ流す)で、b.example.test が次の4つを発行します。

b.example.test の /set が返す Set-Cookie(lab/srv.js)設定ファイル
c_none=exp4; SameSite=None; Secure; Path=/
c_lax=exp4; SameSite=Lax; Secure; Path=/
c_strict=exp4; SameSite=Strict; Secure; Path=/
c_unspec=exp4; Secure; Path=/

続いて a.example.test のページのフォームから b.example.test へ POST し、対照として a.test からも同じ宛先へ POST しました。同じことを example.com 系の名前でも行っています(外部には接続していません)。

サーバログの POST 行(Chromium 153 trial 1。JSON から host・origin・sec-fetch-site・cookie だけを抜き出して整形)コマンド出力
b.example.test origin=https://a.example.test:8443 sec-fetch-site=same-site
cookie: c_none=exp4; c_lax=exp4; c_strict=exp4; c_unspec=exp4
b.example.com origin=https://a.example.com:8443 sec-fetch-site=same-site
cookie: c_none=exp4com; c_lax=exp4com; c_strict=exp4com; c_unspec=exp4com
b.example.test origin=https://a.test:8443 sec-fetch-site=cross-site
cookie: c_none=exp4; c_unspec=exp4

origin が別なのに c_strict が付いています。 ブラウザ自身も sec-fetch-site で same-site と申告しています。別の site である a.test から来た要求では、Lax と Strict が外れました。属性なしの c_unspec が残っている理由は、後で見ます。Firefox 155・WebKit 26.6(どちらも Playwright 同梱版)でも各2回、Cookie の並び順を除いて同じ結果です。

Cookie は Domain 属性なしなので、送り先は b.example.test に限られます。「どのホストへ送るか」と「どこから来た要求か」は別の判定で、SameSite が見ているのは後者です。

✓ ここまでの確認

`https://b.example.test` の Cookie に `SameSite=Strict` を付けました(Domain 属性なし)。`https://a.example.test` のページから b.example.test へ要求が出たとき、この Cookie はどうなりますか。

抽象原理

境界の位置も、既定値も、ブラウザ側の知識で決まる

SameSite の判定には、ブラウザが持っている知識が2つ入り込みます。

1つ目は PSL です。URL Standard は、registrable domain について次のように注意しています。

The notion of "public suffix" and "registrable domain" cannot be relied-upon to provide a hard security boundary, as the public suffix list will diverge from client to client.

site の境目は、ブラウザが持つ一覧の版で動きえます。

2つ目は、属性を書かなかった Cookie の扱いです。6265bis は属性なしの Cookie の same-site-flag を Default とし、取り出しの手順で Lax と同じ扱いにします。そのうえで、互換のための緩いモードを認めています。

user agents MAY apply "Lax-allowing-unsafe" enforcement only to cookies that did not explicitly specify a SameSite attribute

適用するなら最近作られた Cookie に限るべき(SHOULD)とし、2 minutes or less を運用経験上の目安として挙げます。2分は MAY のモードの中の SHOULD の目安で、上限の規定ではありません。

仕様は Lax 自体の効き目にも限定を付けています。does not offer a robust defense against CSRF as a general category of attack(§5.6.7.1)。Strict についても、それだけを CSRF 対策の全部にしないよう求めています(§8.8.1)。

具体実物

属性を書かなかった Cookie は、エンジンで分かれる

b.test が発行した直後に a.test から POST し、125秒待ってもう一度 POST しました(Playwright 同梱版の headless。市販のブラウザそのものではありません)。

b.test/counter に届いた POST の cookie(各エンジン trial 1。trial 2 も同じ結果)コマンド出力
chromium 153.0.8010.12 t+0s c_none=wchromium1; c_unspec=wchromium1
t+125s c_none=wchromium1
firefox 155.0 t+0s c_none=wfirefox1; c_unspec=wfirefox1
t+125s c_none=wfirefox1; c_unspec=wfirefox1
webkit 26.6 t+0s c_none=wwebkit1; c_unspec=wwebkit1
t+125s c_none=wwebkit1; c_unspec=wwebkit1

一次ソースと突き合わせると、次のようになります。

属性なしの Cookie の扱い(ソースは 2026-09-23〜24 時点の main)表
エンジン 一次ソース 実測(125秒後)
Chromium 既定は Lax。作成2分以内は Lax-allowing-unsafe 付かない
Firefox network.cookie.sameSite.laxByDefault の既定は false 付く
WebKit lax-by-default に当たる設定が見当たらない 付く

Chromium は kLaxAllowUnsafeMaxAge = base::Minutes(2) で、仕様の目安と一致します。ただし LEGACY のアクセス方式では None 扱いに戻る分岐がソースにあります。chromestatus では M85 で既定有効、2020年7月14日から段階的に展開とされています。

WebKit は、Apple 版の Cookie 処理が非公開の CFNetwork 側にあるため、オープンソースだけでは製品版 Safari の挙動を確定できません。Edge も確かめていません。

✓ ここまでの確認

「今のブラウザは属性なしの Cookie を Lax として扱うので、SameSite を書かなくてよい」という主張の扱いとして適切なのはどれですか。

⇄ 2つを見比べる
具体例 A

SameSite の Cookie

a.example.test から来た POST に、b.example.test の Strict の Cookie が付きました。兄弟から来た要求は、Cookie の持ち主と同じ側として扱われます。

線の位置表
比べる単位 site(scheme + registrable domain)
a.example.test → b same-site(Strict も付く)
a.test → b cross-site(Lax・Strict は外れる)
具体例 B

Site Isolation のプロセス分離

ブラウザの仕組み『分離の単位は origin ではなく site』で見たとおり、Chromium の分離も site 単位でした。a.example.com と b.example.com は同じ site なので、同じプロセスを共有しえます。

線の位置表
比べる単位 site(scheme + eTLD+1)
a.example.com と b 同じプロセスを共有しうる

Cookie の送信判定とプロセス分離。仕組みは違いますが、兄弟サブドメインの扱いに共通するのは何でしょうか。

「サブドメインを分けた」ことで、それぞれの仕組みの線の外に出られるかを考えてください。

この章のまとめ

  • SameSite が比べるのは site = (scheme, registrable domain)。ポートとサブドメインは入らない。
  • a.example.test から来た POST に b.example.test の Strict の Cookie が付いた(3エンジン×2回)。別の site の a.test からでは Lax と Strict が外れた。
  • .test は PSL に無く、既定規則 * で test が public suffix になる。site の境目は PSL の版で動きうる。
  • 属性なしの扱いはエンジンで違う。Chromium は Lax(作成2分以内は Lax-allowing-unsafe)、Firefox の既定値は false、Playwright 同梱の WebKit でも Lax 扱いされなかった。製品版 Safari と Edge は確かめていない。
  • 6265bis の「2 minutes」は MAY のモードの中の SHOULD の目安。
  • SameSite は「どこから来たか」を site の粒さで見る仕組みで、「本人が望んだか」は見ていない。

自分の現場で確かめる

読んだだけでは身につきません。手元の環境で実際に見てみてください。

理解度チェック

読んだ直後は「分かった」と感じやすいものです。実際に解いて確かめてください。

問題 1 / 3正解: 0

OWASP の CSRF Prevention Cheat Sheet は、SameSite の限界の1つとして何を挙げていますか。

用語集(4語)
site
HTML Standard の定義で (scheme, registrable domain) の組。ポートとサブドメインは比較に入らない。ブラウザの仕組み『分離の単位は origin ではなく site』の scheme + eTLD+1 と同じもの。
registrable domain(eTLD+1)
public suffix に左のラベルを1つ足したもの。どこまでが public suffix かは Public Suffix List で決まる。
Public Suffix List
com や co.uk のように、登録者が別々になる接尾辞の一覧。どの規則にも当たらないときは既定規則 `*` で最右のラベルを public suffix とする。
Lax-allowing-unsafe
6265bis が認める互換モード。属性なしの Cookie を、トップレベル要求ならメソッドを問わず送る。適用は MAY で、最近作られた Cookie に限るべきとされる。