セッションとCSRF対策の実装判断

中級 | 12分 で読める | 2026.06.27

公式ドキュメント

CookieセッションではCSRFを考える

Cookieは条件に合うリクエストへ自動送信されます。これは便利ですが、CSRFの前提にもなります。

Cookieセッションでは、SameSiteだけ、CSRF tokenだけ、Origin検証だけに寄せず、構成に応じて組み合わせます。

CSRFの本質:

被害者はログイン済み
外部サイトが被害者のブラウザにリクエストを送らせる
Cookieが自動で付く
サーバーが本人の操作だと誤認する

まずGETで状態変更しない

最初の原則は、GETで状態変更しないことです。

避けたい例:

GET /account/delete
GET /logout
GET /settings/email?value=...

GETはリンク、画像、プリフェッチ、クローラーなどで意図せず呼ばれる可能性があります。

状態変更はPOST、PUT、PATCH、DELETEなどで扱います。

POST /logout
POST /settings/email
DELETE /account

これはCSRF対策の前提です。

SameSiteの位置づけ

SameSite=Lax は、多くの通常セッションで有効な防御になります。

Set-Cookie: session_id=...; HttpOnly; Secure; SameSite=Lax

Laxでは、多くのクロスサイトPOSTでCookieが送られにくくなります。通常のフォームCSRFに対して効果があります。

ただし、SameSiteだけに依存しない方がよい場面があります。

  • 古いブラウザ対応
  • OAuth/OIDC callback
  • サブドメインをまたぐ構成
  • SameSite=None が必要なSSO
  • GETで副作用がある古いAPI
  • 高リスク操作

SameSiteは強力ですが、アプリケーションの意図を知っているわけではありません。

CSRF token

CSRF token は、正規画面から発行されたリクエストかを確認するためのランダム値です。

フォーム例:

<form method="post" action="/settings/email">
  <input type="hidden" name="csrf_token" value="random_token">
  <input name="email">
  <button>変更</button>
</form>

サーバー側:

sessionに保存したcsrf_tokenと
フォームから送られたcsrf_tokenが一致するか確認

重要なのは、攻撃者が外部サイトからそのtokenを知れないことです。

Synchronizer Token Pattern

サーバー側sessionにCSRF tokenを保存する方式です。

session:abc123 = {
  userId: "user_001",
  csrfToken: "random"
}

POST時:

Cookieのsession_idでsessionを読む
フォームのcsrf_tokenを読む
session.csrfTokenと一致すれば通す

Cookieセッションと相性がよく、初学者にも理解しやすい方式です。

Double Submit Cookie は、CSRF tokenをCookieとリクエスト本文・ヘッダーの両方で送る方式です。

Cookie: csrf_token=random
X-CSRF-Token: random

サーバーは両者が一致するか確認します。

ただし、単純なDouble SubmitはCookie injectionなどの注意点があります。署名付きtokenやsessionと紐づいたtokenにするなど、実装の安全性を確認します。

Origin / Referer 検証

状態変更リクエストでは、Origin ヘッダーを確認する方法もあります。

Origin: https://app.example.com

サーバー側で期待するOriginかを見ます。

Origin が https://app.example.com なら許可
それ以外なら拒否

Origin がない場合の扱い、プロキシ、古いブラウザ、同一オリジンフォームなどを考慮します。多くのWebアプリでは、CSRF tokenとOrigin検証を組み合わせると堅くなります。

SPAとAPIの場合

SPAでCookie認証のAPIを呼ぶ場合、CSRF対策は必要です。

例:

await fetch("/api/settings/email", {
  method: "POST",
  credentials: "include",
  headers: {
    "Content-Type": "application/json",
    "X-CSRF-Token": csrfToken
  },
  body: JSON.stringify({ email })
});

このとき、CSRF tokenをどこから取得するかを決めます。

候補:

  • 初期HTMLに埋め込む
  • /api/csrf-token で取得する
  • HttpOnlyではないCSRF専用Cookieを読む

認証CookieはHttpOnlyにし、CSRF tokenは別扱いにします。CSRF tokenは認証情報そのものではなく、リクエストが正規UIから出ていることを確認するための値です。

ログアウトもPOSTにする

ログアウトは状態変更です。

POST /logout

GETログアウトは、外部リンクやプリフェッチで意図せず発火する可能性があります。

ログアウトは被害が小さく見えますが、ユーザー体験を壊し、ログイン状態を変える操作です。CSRF方針に合わせてPOSTで扱うのが自然です。

高リスク操作は再認証も使う

CSRF tokenがあれば何でも安全、とは考えません。

高リスク操作では、再認証やMFAを組み合わせます。

  • パスワード変更
  • メール変更
  • 支払い方法変更
  • 退会
  • APIキー作成
  • 管理者権限操作

CSRFは「意図しないリクエスト」を防ぐ対策です。本人端末が乗っ取られている、XSSがある、セッションが盗まれている場合は別の対策が必要になります。

実装判断の目安

構成推奨
通常の同一siteフォームSameSite=Lax + CSRF token
管理画面CSRF token + Origin検証 + 再認証
SPA + Cookie APIcredentials + CSRF header + Origin検証
SSO / 外部連携ありSameSite条件を個別確認
GET副作用が残る旧APIまず設計を修正

CSRF対策は、アプリの構成で変わります。テンプレートを丸写しせず、Cookieがいつ送られるか、サーバーが何を本人操作とみなすかを確認します。

まとめ

Cookieセッションでは、CSRFを前提に設計します。

  • GETで状態変更しない
  • SameSite=Lax を基本に検討する
  • CSRF tokenで正規画面からの送信を確認する
  • Origin / Referer検証も組み合わせる
  • SPAではCSRF headerを設計する
  • ログアウトもPOSTにする
  • 高リスク操作は再認証を検討する

SameSite、CSRF token、Origin検証、再認証を役割ごとに分けると、実装判断が安定します。

参考リソース

次に読む記事

← 一覧に戻る
PR
PR
PR
PR