中級では「CSRFトークンとSameSiteがなぜ効くか」を見ました。上級では、SameSiteの三値・トークン方式の内部差・API/JSONへのCSRF・ログインCSRFまで、仕組みを一段深く掘ります。全体像は Webのセキュリティ(全体像)、認証との違いは 認証 を参照。
上級の解説は準備中のため、上級の内容を表示しています。
概要 — まず全体をつかむ
詳細 — 1段階ずつ追う
SameSiteの三値と既定
SameSite Cookie は「クロスサイト起点の送信でCookieを付けるか」を制御します。値は3つです。
- Strict — クロスサイト起点なら一切付けない。最も堅いが、外部リンクから来た直後はログイン切れに見えるなど体験を損なう
- Lax(多くのブラウザの既定)※1 — トップレベルのGET遷移(リンクを踏むなど)には付けるが、フォームPOST・画像・iframe・fetch など副作用を伴いやすい送信には付けない
- None — 常に付ける。クロスサイトで必要な場面(外部埋め込みなど)に使うが、
Secure必須で、CSRF的にはトークン等が別途要る
アニメーション『CSRFの流れ(ログイン中を悪用)』を開く
- 被害者ブラウザ — 正規サイトにログイン中で、そのサイトのCookieを持っている利用者のブラウザ
- 正規サイト — 本人がログインしているサイト。Cookieが付いていれば本人の操作と信じてしまう
- 罠サイト — 攻撃者が用意したサイトやメール。被害者に勝手なリクエストを踏ませる
既定がLaxになったことで素朴なフォームPOST型CSRFの多くは自動的に緩和されました。ただしLaxは万能ではありません。トップレベルGET遷移には付くため、副作用のあるGET(後述)や、Lax適用前の猶予・古いブラウザでは抜けます。だからトークンと併用が基本です。
同期トークンと二重送信クッキー
「正規の画面から始まった操作か」を確かめるトークンにも、二つの実装があります。仕組みと強弱が違います。
- 同期トークン(synchronizer) — サーバがセッションに正解値を保持し、フォームに埋めた値と突き合わせる。サーバ状態を持つぶん堅いが、サーバ側の管理が要る
- 二重送信クッキー(double-submit) — トークンをCookieとリクエスト本体の両方に入れ、一致するかだけを見る。サーバが状態を持たなくてよく水平スケールしやすい一方、サブドメインからのCookie上書きや中間者に弱く、署名付きや
__Host-接頭辞のホスト限定Cookieで補強する
粒度も設計点です。
- セッション単位 — ログイン中は同じトークンを使い回す。実装が軽い
- リクエスト単位 — 操作ごとに使い捨てる。漏洩時の影響を最小化できるが、複数タブや戻る操作と相性が悪く、実装が重い
トークンの置き場所は、URLに載せると Referer 経由で漏れうるため、フォーム値かカスタムヘッダで渡します(次節に続く)。
APIとJSONへのCSRF
「JSON APIならCSRFは無関係」は誤解です。ただし守りやすくはなります。
- カスタムヘッダ要求 —
X-Requested-Withや独自ヘッダの付与を必須にする。クロスサイトの単純なフォームやimgでは任意ヘッダを付けられないため、これだけで多くのCSRFを弾ける - プリフライトとCORSの関係※2 — カスタムヘッダや
application/jsonを伴うクロスオリジンのfetchは、ブラウザが先に プリフライト(OPTIONS) を送る。サーバが許可を返さなければ本リクエストは飛ばない。つまりCORSの許可設定が、意図しないクロスサイト送信の関所になる - 落とし穴 — Content-Typeを
text/plain等にするとプリフライトを回避される「単純リクエスト」の枠に入りうる。サーバ側でContent-Typeを厳格に検証し、CORSを緩め過ぎない
CORSは「読み取りを許すか」の仕組みで、CSRF対策そのものではありません。しかしプリフライトとカスタムヘッダ必須を組み合わせると、実質的にCSRFの強い抑止になります。
登場人物メモ:
- ※1 Lax — SameSiteの既定値。クロスサイトのトップレベルGET遷移にはCookieを付けるが、フォームPOSTや埋め込みには付けない
- ※2 プリフライト — カスタムヘッダなどを伴うクロスオリジン要求の前に、ブラウザが許可を確かめるために先に送るOPTIONS要求
ログインCSRFとトップレベル遷移
CSRFは「ログイン後の操作」だけではありません。
- ログインCSRF — 攻撃者が自分の資格情報で被害者を勝手にログインさせ、以降の入力(検索履歴・カード登録など)を自分のアカウントに吸い上げる。ログインフォームにもトークンを置くのが対策
- トップレベル遷移の悪用 — Lax下でもGETのトップレベル遷移にはCookieが付く。だから副作用のあるGET(
/logoutや/transfer?...)を用意すると、単なるリンク誘導でCSRFが成立しうる
なぜGETは安全であるべきか・多層防御
- GETは「取得だけ」に保つ — GETは仕様上、繰り返しても副作用のない安全なメソッドであるべき。状態変更をGETに載せると、画像タグ・プリフェッチ・リンク誘導だけで発火し、SameSite=Laxすら素通りする
- 多層で重ねる — SameSite(前提を崩す)+トークン(攻撃者が用意できない情報を必須にする)+Origin/Referer確認(発生源の照合)+重要操作の再認証(MFA)を積む。一枚が破れても次で止める。守り全体の位置づけは Webの中のセキュリティ を参照
- SameSite過信 — 既定Laxは万能ではない。トップレベルGET・古いブラウザ・例外遷移で抜けるため、トークン併用が基本
- 二重送信の署名忘れ — 素の二重送信はCookie上書きに弱い。署名や
__Host-接頭辞で固める
理解度チェック
そのまま解けます(成績は保存されません)。無料アカウントを作ると、学習の記録と進捗の山登りが始まります。
問1. SameSite=Lax(多くのブラウザの既定)の挙動として正しいのは?
問2. 同期トークン(synchronizer)と二重送信クッキー(double-submit)の違いとして正しいのは?
問3. SameSite=Strictの特徴として正しいのは?
問4. SameSite=Noneを使うときに必須となるのはどれ?
問5. /logout や /transfer のような「副作用のあるGET」を用意すると危険なのはなぜ?
問6. 二重送信クッキー方式の弱点と補強の組み合わせとして正しいのは?
問7. カスタムヘッダや application/json を伴うクロスオリジンのfetchの前に、ブラウザが許可を確かめるため先に送るリクエストのHTTPメソッドは?大文字英字で答えてください。
問8. 攻撃者が自分の資格情報で被害者を勝手にログインさせ、以降の入力を自分のアカウントに吸い上げる攻撃を「◯◯◯◯CSRF」と呼ぶ。◯◯◯◯に入る語は?