1. Cookie とは #what-is-cookie
Web では HTTP プロトコル自体に「ログイン状態を保持する」仕組みがありません。そこで Cookie が使われます。
- ユーザがログインフォームに ID/パスワードを送信
- サーバが認証成功を確認し、レスポンスヘッダ
Set-Cookie: session=abc123を返す - ブラウザは
session=abc123という Cookie を保存 - 次回以降、同じドメインへのリクエストには自動的に
Cookie: session=abc123が付く - サーバはこの Cookie を見て「これは ログイン済みのユーザだ」と判断
2. Cookie の自動送信ルール #auto-send
ブラウザは「リクエスト先のドメイン」を見て、そのドメインに紐付く Cookie を全て自動的に添付します。リクエストを発行した HTML がどこにあったかは関係ありません。
example.com にあるページから shop.utsuroi.example.com へリクエスト → shop.utsuroi.example.com の Cookie が自動添付される evil.example にあるページから shop.utsuroi.example.com へリクエスト → shop.utsuroi.example.com の Cookie が 同じく自動添付される
攻撃者の罠サイト(evil.example)が、被害者のブラウザに対して shop.utsuroi.example.com への HTTP リクエストを発行させると、被害者がうつろい EC にログイン中であれば、その認証 Cookie が自動的に添付されます。サーバは「正規のユーザからのリクエスト」と判断してしまう。
3. Cross-Origin リクエスト #cross-origin
HTML の中には、別オリジンへのリクエストを発行する要素が多数あります:
<img src="https://other-site.com/...">— 画像読み込み(GET リクエスト)<script src="https://other-site.com/...">— JS 読み込み(GET リクエスト)<form action="https://other-site.com/..." method="POST">— フォーム送信(POST リクエスト)<link href="https://other-site.com/...">— CSS や favicon 読み込み
重要なのは、JavaScript なしでも HTML だけで cross-origin リクエストが発生すること。<img> や <form> はブラウザが古典的に許可している cross-origin リクエストです。
<form action="https://shop.utsuroi.example.com/account/email" method="POST"> <input name="email" value="attacker@evil.example"> </form> <script>document.forms[0].submit();</script>
上の HTML を攻撃者の罠サイトに置けば、訪問者のブラウザは自動的に shop.utsuroi.example.com へ POST します。被害者の認証 Cookie 込みで。
4. Same-Origin Policy の役割と限界 #same-origin-policy
Same-Origin Policy(SOP)は、ブラウザの基本的なセキュリティ機構。「JavaScript は別オリジンのリソースのレスポンス内容を読めない」というルール。
これは XSS の文脈では「攻撃者サイトの JavaScript からは うつろい EC のページを読めない」と理解されますが、CSRF 文脈では別の側面が重要です:
- SOP は送信を禁じない — フォーム送信、画像読込、スクリプト読込は許可される
- SOP はレスポンス読込を禁じる — でも CSRF はレスポンスを読まない
- 結論: SOP は CSRF を防がない
XSS:被害者サイトの中で攻撃者の JavaScript を実行(被害者サイトのレスポンスを読める)
CSRF:攻撃者サイトから被害者サイトへリクエストを送るだけ(レスポンスは読めなくていい、副作用が起きれば成功)
→ 攻撃者は「なりすましでメール変更」「なりすましで送金」「なりすましで投稿削除」など、サーバ側の状態変更だけが目的。
5. SameSite Cookie 属性 #samesite
SameSite は Cookie に付加できる属性で、cross-site リクエスト時の Cookie 送信をブラウザレベルで制御します。
- SameSite=Strict — cross-site のリクエストには Cookie を一切送らない。最も安全だが UX を損なう(他サイトからのリンクで未ログイン状態になる)
- SameSite=Lax — 「トップレベルナビゲーション(リンククリック等)」では送信、フォーム POST や iframe では送信しない。Chrome のデフォルト(2020年以降)
- SameSite=None; Secure — cross-site でも常に送信。明示的に必要な場合のみ。HTTPS 必須
Chrome のデフォルトが SameSite=Lax になったことで、POST フォームによる古典的 CSRF は攻撃が成立しにくくなりました。ただし、(a) 古いブラウザ、(b) サーバ側で SameSite=None を明示しているサイト、(c) GET リクエストで状態変更するアンチパターン、では依然として CSRF が成立します。SameSite に依存せず、Token による根本対策を併用するのが定石です。