#はじめに

「ログインしたまま悪意のあるサイトを開いたら、勝手に送金されていた」——これがCSRF(クロスサイトリクエストフォージェリ)攻撃です。

Cookieは便利な仕組みですが、何も対策しないと、ユーザーが意図しないリクエストにもCookieが送信されてしまいます。SameSite属性は、この問題を解決するための仕組みです。

この記事を読むと、以下のことができるようになります:

  • CSRF攻撃の仕組みを理解できる
  • SameSite属性の3つの値(Strict、Lax、None)の違いがわかる
  • どの場面でどの値を使うべきかを判断できる

#「サイト」と「オリジン」の違い

SameSite属性を理解するには、まず「サイト」の定義を知る必要があります。

#オリジン(Origin)

オリジンは「スキーム + ホスト + ポート」で構成されます。

https://www.example.com:443
^^^^^^   ^^^^^^^^^^^^^^^  ^^^
スキーム    ホスト        ポート

これらのすべてが一致して初めて「同一オリジン」です。

#サイト(Site)

サイトは「スキーム + 登録可能ドメイン(eTLD+1)」で構成されます。

https://www.example.com
^^^^^^     ^^^^^^^^^^^
スキーム   登録可能ドメイン

「登録可能ドメイン」とは、パブリックサフィックス(.com、.co.jpなど)に1つのラベルを加えたものです。

#比較

URL AURL B同一オリジン同一サイト
https://www.example.comhttps://api.example.com
https://example.comhttps://example.com:8080
https://example.comhttp://example.com
https://example.comhttps://example.org

SameSite属性は「サイト」を基準に判断します。 サブドメインが違っても同一サイトです。

#CSRF攻撃とは

CSRF(Cross-Site Request Forgery)は、ユーザーの意図しないリクエストを、ユーザーの認証情報を使って送信させる攻撃です。

#攻撃の流れ

1. ユーザーがbank.comにログイン(認証Cookieを取得)

2. ユーザーが悪意のあるサイト evil.com を訪問

3. evil.com のページに仕込まれた不正なフォーム:
   <form action="https://bank.com/transfer" method="POST">
     <input type="hidden" name="to" value="attacker">
     <input type="hidden" name="amount" value="100000">
   </form>
   <script>document.forms[0].submit();</script>

4. ブラウザが bank.com にPOSTリクエストを送信
   → このとき、bank.com のCookieが自動的に付与される!

5. サーバーはCookieを見て正規のリクエストと判断し、送金を実行

ユーザーはbank.comにログインしたまま evil.com を訪問しただけで、知らないうちに送金が行われてしまいます。

#SameSite属性とは

SameSite属性とは、クロスサイトリクエストにCookieを送信するかどうかを制御する属性です。

Set-Cookie: session_id=abc123; SameSite=Lax

3つの値があります:

動作
Strictクロスサイトリクエストには一切送信しない
Lax安全なナビゲーション(リンククリックなど)のみ送信
Noneすべてのリクエストで送信(Secure必須)

#Strict

最も厳格な設定です。クロスサイトからのリクエストには、Cookieを一切送信しません。

Set-Cookie: session_id=abc123; SameSite=Strict

#動作例

[evil.com]                         [bank.com]
    |                                   |
    |--- <a href="bank.com">リンク --->|
    |    Cookie: 送信されない ❌         |
    |                                   |
    |--- <form action="bank.com"> ---->|
    |    Cookie: 送信されない ❌         |

#メリット

CSRF攻撃を完全に防げます。

#デメリット

外部サイトからのリンクでも Cookie が送信されないため、ユーザー体験が悪化する可能性があります。

例: Slackに貼られた社内ツールへのリンクをクリック → ログイン状態が維持されず、再ログインが必要

#適した用途

  • 銀行の送金確認
  • パスワード変更
  • 二要素認証の設定

など、セキュリティが最優先で、外部からのアクセスを想定しない操作。

#Lax

バランスの取れた設定です。安全な「トップレベルナビゲーション」のみCookieを送信します。

Set-Cookie: session_id=abc123; SameSite=Lax

#「トップレベルナビゲーション」とは

ブラウザのアドレスバーのURLが変わるような遷移を指します。

操作Cookieの送信
リンクのクリック(<a href>
GETフォームの送信
POSTフォームの送信
<img><iframe>fetch
JavaScriptによるPOSTリクエスト

#動作例

[evil.com]                         [bank.com]
    |                                   |
    |--- <a href="bank.com">リンク --->|
    |    Cookie: 送信される ✅           |
    |                                   |
    |--- <form method="POST"> -------->|
    |    Cookie: 送信されない ❌         |

#メリット

  • 外部リンクからのアクセスでもログイン状態を維持
  • POST/非同期リクエストによるCSRFは防止

#デメリット

  • GETリクエストで状態変更を行う設計だと脆弱
  • 完全な防御ではない

#適した用途

一般的なセッションCookieに最適です。多くのWebアプリケーションではLaxで十分です。

#None

すべてのクロスサイトリクエストでCookieを送信します。

Set-Cookie: session_id=abc123; SameSite=None; Secure

重要: SameSite=Noneを使用する場合、Secure属性が必須です。HTTPSでのみ送信されるようになります。

#動作例

[partner.com]                      [api.example.com]
    |                                   |
    |--- iframe内でapi呼び出し -------->|
    |    Cookie: 送信される ✅           |
    |                                   |
    |--- fetch() API呼び出し ---------->|
    |    Cookie: 送信される ✅           |

#適した用途

  • サードパーティウィジェット(埋め込みコメント、決済フォームなど)
  • 複数ドメインにまたがるSSOシステム
  • 広告・アナリティクス(ただしプライバシー規制に注意)

#注意

SameSite=NoneはCSRF対策を放棄することを意味します。他の対策(CSRFトークンなど)が必要です。

#デフォルト動作

SameSite属性を指定しない場合、ブラウザはどのように動作するでしょうか?

#現在のブラウザの動作

主要なブラウザは、SameSite属性が指定されていない場合、Laxとして扱います。

# 明示的な指定なし
Set-Cookie: session_id=abc123

# ↓ ブラウザは以下と同様に解釈
Set-Cookie: session_id=abc123; SameSite=Lax

これは2020年頃から主要ブラウザで導入された変更です。

#過去との違い

以前は、SameSite属性を指定しないとNoneとして扱われていました。現在のデフォルトLaxは、よりセキュアな方向への変更です。

#実践: SameSite属性の確認

#DevToolsで確認

  1. DevToolsを開く
  2. 「Application」→「Cookies」を選択
  3. 「SameSite」列を確認

表示される値:

  • Strict
  • Lax
  • None
  • 空白(デフォルト、Laxとして扱われる)

#コンソールでの警告

不適切なSameSite設定は、コンソールに警告が表示されます。

A cookie associated with a cross-site resource at https://example.com/
was set without the `SameSite` attribute. It has been blocked...

#SameSite属性の選び方

Cookieの用途は?

    ├── サードパーティでの利用が必要
    │   └── SameSite=None; Secure(+ CSRFトークン)

    └── ファーストパーティのみ

        ├── 外部リンクからのアクセスを許容
        │   └── SameSite=Lax(推奨)

        └── セキュリティ最優先
            └── SameSite=Strict

多くの場合、Laxが最適なバランスです。

#まとめ

  • SameSite属性はCSRF対策の重要な仕組み
  • 3つの値:
    • Strict: クロスサイトでは一切送信しない
    • Lax: 安全なナビゲーションのみ送信
    • None: すべて送信(Secure必須)
  • デフォルトはLax(現代のブラウザ)
  • 多くの場合、Laxが最適
  • Noneを使う場合はCSRFトークンなど追加の対策が必要

#次のステップ

SameSite属性を理解したところで、次はSecure・HttpOnly・Partitioned属性について学びましょう。これらの属性を組み合わせることで、Cookieのセキュリティをさらに強化できます。

#参考リンク