Cookie・セッション・SameSite の基礎 - Secure / HttpOnly と CSRF・CORS の関係

Cookie・セッション・SameSite の基礎 - Secure / HttpOnly と CSRF・CORS の関係

作成日:
読了:11
更新日:

ログイン状態の維持、CSRF対策、クロスオリジンでの認証——どれも根っこは Cookie・セッション・SameSite の理解です。ここが曖昧だと「なぜかログインが切れる」「なぜかCookieが送られない」で延々ハマります。この記事では、MDN・RFC 6265bis・OWASP を一次ソースに、Web認証の基礎を整理します。

Cookieの基本

サーバーはレスポンスの Set-Cookie ヘッダでCookieを設定し、ブラウザは以降のリクエストに Cookie ヘッダで自動送信します。1つのレスポンスに Set-Cookie を複数並べれば複数のCookieを設定できます。

Set-Cookie の基本
Set-Cookie: sessionid=abc123
Set-Cookie: prefs=dark; Max-Age=2592000

有効期限とスコープ

  • Expires: 絶対日時で失効を指定
  • Max-Age: 相対秒数で指定。両方あれば Max-Age が優先
  • どちらも無ければセッションCookie(ブラウザ終了で消える。ただし「セッション復元」機能で残ることがある)
  • Domain: 省略するとそのホストのみDomain=example.com を付けるとサブドメインにも送られる(意図せず広げる事故が多い)
  • Path: パスのプレフィックス一致。なお Pathセキュリティ境界ではない(同一オリジンの別パスからJSでアクセス可能)

セキュリティ属性: Secure と HttpOnly

属性効果防ぐ脅威
SecureHTTPS接続でのみ送信通信経路上の盗聴
HttpOnlydocument.cookie から読めないXSSによるCookie窃取

重要なのは、HttpOnly でもHTTPリクエストには送られること。fetch の通信にCookieは付きますが、JSから値を読むことだけが封じられます。認証用Cookieは原則 Secure; HttpOnly を付けます。

SameSite: Strict / Lax / None

SameSite は「クロスサイトのリクエストでCookieを送るか」を制御します。CSRF対策の要です。

リクエストの種類StrictLaxNone
同一サイト送信送信送信
別サイトからのリンク(GETナビゲーション)ブロック送信送信
別サイトからの POST フォームブロックブロック送信
別サイトからの fetch / XMLHttpRequestブロックブロック送信
別サイトの img / script 読み込みブロックブロック送信
  • Strict: クロスサイトでは一切送らない(最も安全。ただし外部リンクから来ると未ログイン扱いになる)
  • Lax: トップレベルのGETナビゲーション(アドレスバーが変わる遷移)だけ許可。大半のCSRFを緩和しつつ実用的
  • None: クロスサイトでも送る。Secure が必須SameSite=None 単体はブラウザに拒否される)

NOTE

Chrome 80(2020年2月)以降、SameSite を明示しないCookieは Lax 扱いになりました。挙動をブラウザ既定に委ねず、常に明示するのが安全です。

用途別の Set-Cookie
# 認証セッション(推奨形)
Set-Cookie: session=38afes7a8; Secure; HttpOnly; SameSite=Strict; Path=/
 
# サードパーティ埋め込み等でクロスサイト送信が要る場合
Set-Cookie: widget=7yjgj; Secure; HttpOnly; SameSite=None

セッション管理

典型的なサーバー側セッションは、セッションIDだけをCookieで渡し、本体(ユーザー情報など)はサーバー側ストア(DB・Redis等)に置きます。

  • セッションIDは予測不可能な値(CSPRNGで十分なエントロピー)にする
  • ログイン成功時にセッションIDを再生成する——これがセッション固定攻撃の根本対策。SecureHttpOnly を付けるだけでは固定攻撃は防げません
  • セッション認証情報を localStorage 等のWeb Storageに置かない(XSSで抜かれる)

トークンベース(JWTなど)との使い分けはJWT の仕組みと正しい使い方を、パスワードに依存しない認証はパスキー / WebAuthnもどうぞ。

CSRFとの関係(SameSiteだけでは不十分)

SameSite=Strict/Lax はクロスサイトからの自動Cookie送信を抑えるので、CSRFを大きく緩和します。ただしこれ単体では守りきれません

  • Lax は安全なGETを通す: GETで状態を変更するAPIがあると素通りする
  • サブドメインは同一サイト扱い: evil.example.com を乗っ取られると app.example.com 宛に送られうる
  • 古い/組み込みブラウザSameSite を無視することがある

結論として、SameSiteは多層防御の一層。状態変更は CSRFトークン(またはDouble Submitパターン)と併用し、GETで副作用を起こさない設計を守ります。

CORSとCookieの関係

クロスオリジンの fetch でCookieを送受信するには、4つすべてが必要です(CORS の仕組みの続きとして重要)。

条件設定場所
credentials: "include"クライアント(fetch
Access-Control-Allow-Credentials: trueサーバー応答
Access-Control-Allow-Origin: <具体的オリジン>サーバー応答(* 不可)
SameSite=None; SecureSet-Cookie

どれか1つ欠けても送られません。とくに「クライアントで include にしたのにサーバー側の Allow-Credentials を忘れる」「Allow-Origin: * のまま」という取りこぼしが定番です。

Cookieプレフィックス: __Host- と __Secure-

名前の先頭に特定の接頭辞を付けると、ブラウザが属性の条件を強制します(RFC 6265bis)。

プレフィックス強制される条件意味
__Secure-Secure 必須・HTTPS必須Secureで設定されたことを保証
__Host-Secure 必須・Domain 指定禁止・Path=/ 必須オリジンに固定する最も厳しい設定
__Host- プレフィックス(最も堅い)
Set-Cookie: __Host-SID=<token>; Secure; HttpOnly; SameSite=Strict; Path=/

認証Cookieには __Host- を使うと、Domain偽装やHTTP経由の上書きを構造的に防げます。

よくある落とし穴

  • Domain の付け忘れ/付けすぎ: 未指定はホスト限定、指定はサブドメインに拡散。意図と逆になりがち
  • SameSite=NoneSecure なし: 拒否される。HTTP環境では None は使えない
  • セッション固定をCookie属性で防ごうとする: 防げない。ログイン後のID再生成が必須
  • CORSの認証条件を一部だけ満たす: 4条件は全部そろえる
  • Allow-Origin: * と credentials の併用: ブラウザが拒否。具体的オリジンを返す

まとめ

  • Cookieは Set-Cookie で設定し自動送信。Domain 未指定はホスト限定Max-AgeExpires に優先
  • 認証Cookieは Secure; HttpOnlyHttpOnly でも通信には乗る(JSが読めないだけ)
  • SameSite は Strict/Lax/NoneNoneSecure 必須。Chrome 80以降は未指定=Lax
  • セッションはIDだけCookie・本体はサーバーログイン後にID再生成で固定攻撃を防ぐ
  • SameSiteだけでCSRFは防ぎきれない。トークンと併用。クロスオリジンCookieはCORSの4条件
  • 認証Cookieは __Host- 接頭辞でさらに堅く

属性ひとつの意味を押さえるだけで、「ログインが切れる」「Cookieが送られない」の大半は理由が見えてきます。既定に委ねず、明示して堅く設定するのが要です。

参考リンク

CORS の仕組みとハマりどころ - プリフライト・credentials・Allow-Origin を理解する

CORS の仕組みとハマりどころ - プリフライト・credentials・Allow-Origin を理解する

10

CORS(オリジン間リソース共有)を実務目線で整理します。同一オリジンポリシーとの関係、単純リクエストとプリフライト(OPTIONS)の条件、Access-Control-Allow-Origin などの各ヘッダ、credentials 付きで * が使えない理由、Vary: Origin とキャッシュ、そして「No Access-Control-Allow-Origin header」エラーの意味と対処まで、MDN と WHATWG Fetch Standard を一次ソースにまとめます。CORS はブラウザの仕組みであって認可ではない、という勘所も。

JWT(JSON Web Token)の仕組みと正しい使い方 - 署名・検証とセキュリティの落とし穴

JWT(JSON Web Token)の仕組みと正しい使い方 - 署名・検証とセキュリティの落とし穴

12

JWT(JSON Web Token)を実務目線で整理します。header.payload.signature の3部構造と Base64URL、iss/sub/aud/exp などのクレーム、HS256 と RS256/ES256 の使い分け、検証の流れ、そして alg:none 攻撃・アルゴリズム混同・「JWT は暗号化ではないので中身は誰でも読める」という誤解・失効の難しさといった落とし穴まで、RFC 7519/7515/7518/8725 と OWASP を一次ソースにまとめます。

REST API設計の基礎と冪等性 - HTTPメソッドの意味と Idempotency-Key

REST API設計の基礎と冪等性 - HTTPメソッドの意味と Idempotency-Key

10

REST API設計の基礎を、HTTPメソッドの意味(safe / idempotent)から整理します。リソース指向のURL設計、GET/POST/PUT/PATCH/DELETE の使い分けと冪等性の早見表、なぜ冪等性がリトライや二重課金防止に重要か、POST を安全にリトライする Idempotency-Key の仕組み、ステータスコードの使い分け、RFC 9457 のエラー形式まで、RFC 9110・MDN・Stripe を一次ソースにまとめます。