zu-min.com

SameSite Cookie の具体例

cookiejavascriptsamesite

SameSite Cookie の設定と、Cookie が送信されるパターンについて、具体例を挙げて試してみました。

ログイン状態などのセッション情報を維持したい場合、Cookie を設定して状態を維持すると思います。同じサイト内で利用する場合は問題ないのですが、悪意ある第三者のサイトから Cookie を発行したサイト(以降オリジンサイトと言います)に遷移した場合のことを考える必要があります。

特に何もしない状態だと、第三者のサイトからオリジンサイトへ遷移した場合、オリジンサイトはログインした状態で表示されます。さらに遷移時にパラメーターが設定されていると、ログイン状態で不正な操作を行えてしまう可能性があります(CSRF)。

ですので、第三者のサイトからオリジンサイトへの遷移時は Cookie 送信をしないようにする仕組みがあると安全です。その Cookie 送信を抑制する設定が SameSite になります。

SameSite の設定値

Cookie を設定する場合は Set-Cookie 応答ヘッダを使用します。このときに設定するオプションに SameSite があります。

Set-Cookie: key=value; SameSite=Strict; Secure; HttpOnly

SameSite の設定値は3つ存在します。また、 SameSite を指定しないパターンも存在します。未指定の場合は Lax と同等になる予定のようですが、現在は互換性のために若干異なる挙動をします。

設定値説明
Strictもっとも厳密な動作をします。
LaxStrict よりすこし緩和する設定です。(デフォルト)
None制限なしですが、 Secure の場合のみ設定可能です。
未指定Set-Cookie で SameSite を設定しない場合です。

SameSite - 同じサイトとは

遷移先が SameSite 、つまり「同じサイト」であるかの判定に使われるのはドメインですが、完全一致ではありません。ドメインを取得するときに決める部分までで、サブドメインは含みません。

具体的には a.example.com と b.example.com は TLD (トップレベルドメイン) の com と次の階層の example が等しいので SameSite となります。example.com と zu-min.com は TLD は同じですが二階層目が異なるので別のサイトという判定になります。一般的に TLD の次の階層までが同じであればそのドメインの管理者は同じであるためです。

.co.jp とかはどうなるのかというと .co.jp までが TLD (正確には eTLD) となります。どこまでが eTLD になるかは Public Suffix List によって管理されているようです。

遷移パターン

MDN を見ると、遷移パターンとしては normal cross-site subrequests (クロスサイト・サブリクエスト)と navigating to the origin site (オリジンサイトへの遷移)の2種類が書かれています。これだけだといまいち想像ができないので、いくつか具体的な遷移例を書き出してみます。

  • クロスサイト・サブリクエスト
    • iframe や img
    • Ajax (XMLHTTPRequest や fetch API)
  • オリジンサイトへの遷移
    • a タグでの移動
    • form タグでの移動
      • GET / POST
    • window.location.href の変更
    • リダイレクト(Location ヘッダー)

思いつくものを挙げて分類してみましたが、分類と具体例の紐づけに関しては MDN の著者と同じ認識であるかは確証がないので参考程度でお願いします。

遷移パターンと Cookie が送信されるかの組み合わせは以下の通りです。

遷移パターンStrictLaxNone
クロスサイト・サブリクエスト--送信
オリジンサイトへの遷移-送信1送信

実際に試してみる

実際にコードを書いて試してみた結果が下記の表です。第三者のサイトから各遷移パターンによってオリジンサイトへアクセスしたときに、要求ヘッダの Cookieに値が設定されるかを確認しています。

今回は Edge (Chromium) 103.0.1264.44 で調査しました。

遷移パターン分類StrictLaxNone
iframe や imgCS--送信
AjaxCS--送信
a タグNO-送信送信
form タグ (GET)NO-送信送信
form タグ (POST)NO--送信
window.location.hrefNO-送信送信
リダイレクトNO-送信送信
  • CS: クロスサイト・サブリクエスト
  • NO: オリジンサイトへの遷移
  • Ajax で試す場合は credentials: "include" の指定が必要です。
  • Ajax は一応 POST や PUT メソッドも試しましたが同じ結果でした。
  • form に関して、 submit ボタンをユーザーが押すのと JavaScript の submit() で送信されるのでは違いありませんでした。

大体想像通りでしたが、 form の POST は GET と挙動が異なるようです。

また、 a タグで遷移後にユーザー操作による更新(ブラウザの更新ボタンや F5)をしても Strict の Cookie は送信されませんでした。 恐らくユーザーによる更新は第三者サイトからのリクエストを再送するリスクがあるためだと思います。

リダイレクト

HTTP ヘッダ (Location) によるリダイレクトは操作開始時点の URL がオリジンであれば SameSite 判定となり Cookie が送信されるようです。たとえば a.example.com のページに zu-min.com のリンクが張られており、それをクリックして zu-min.com が Location ヘッダを返して a.example.com に戻ってきた場合は Strict の Cookie も送信されます。

ただ、 JavaScript による移動 (location.href や location.replace(url)) の場合は、 JavaScript を実行したページから遷移(ナビゲート)した扱いになるようです。

手順Strict
3rd --[aタグ]-> 3rd --[リダイレクト]-> 1st-
3rd --[aタグ]-> 1st --[リダイレクト]-> 1st-
1st --[aタグ]-> 3rd --[リダイレクト]-> 1st送信
3rd --[aタグ]-> 3rd --[jsで遷移]-> 1st-
3rd --[aタグ]-> 1st --[jsで遷移]-> 1st送信
1st --[aタグ]-> 3rd --[jsで遷移]-> 1st-

下線がスタート位置とみなされる模様

1st: Cookie を設定したサイト
3rd: ほかのサイト
[a タグ]: a タグをクリックして遷移
[リダイレクト]: Location ヘッダを使ってリダイレクト
[jsで遷移]: window.location を使って遷移

他のアプリからの遷移

ブラウザ以外のアプリに貼ってあるリンクをクリックして遷移するとどうなるかというと、初回アクセス(URL 直入力などでアクセス)した時と同じ挙動になるようです。つまり Strict 指定の Cookie も送信されます。

SameSite 未指定の挙動

SameSite 未指定時は基本的に Lax と同じ挙動ですが、1点だけ異なります。Cookie 設定から2分以内であれば form タグの POST でも Cookie が送信されます。値を新規設定、または変更してから 2分以内のようです。同じ値を設定した場合はリセットされませんでした。

ただしこれは一時的な経過措置のため、 いずれは廃止されるようです。

上記の話は SameSite での挙動についてです。

プライバシーの観点から、ブラウザにはサードパーティ Cookie のブロック機能があります。 SameSite が None でも、ユーザーの行動をトラッキングする目的の場合は Cookie 送信がブロックされることがあります。

Footnotes

  1. 一部例外あり。下記表を参照。

関連記事