FIG-071

CSRF — 押した覚えのない送金が通ってしまう

セキュリティ 2026.08.25 公開 読了 約11分

銀行サイトにログインしたまま、別のタブで見知らぬサイトを開いた――その瞬間に、自分の口座から勝手に送金が実行される。CSRF(クロスサイト・リクエスト・フォージェリ)は、こんな「意図しない操作の代行」を成立させてしまう脆弱性です。

仕掛けはとても単純で、ブラウザがCookieを自動で付けて送ってしまう性質を悪用しているだけ。攻撃者はあなたのパスワードを知る必要すらありません。下の図1で、罠サイトのボタンを押して何が起きるかを見てください。そのあと対策のスイッチを入れて、同じ攻撃が通らなくなるところまで試せます。

CSRF — Cookieは「どのサイトが送らせたか」を区別しない
🏦 bank.example 😈 free-gift.example
「今すぐ100万円が当たる!」
<form action="https://bank.example/transfer" method="POST">
POST /transfer amount=1000000 Cookie: session=ok token: なし
bank.example サーバー
? セッションは有効か
? CSRFトークンは正しいか
? 同一サイトからの送信か
口座残高 1,500,000
リクエスト待ち
READY
罠サイトのボタンを押してみる
あなたは bank.example にログイン済み(セッションCookieを持っています)。その状態で罠サイトのボタンを押すと、罠サイトのフォームが銀行サーバーへ直接POSTを飛ばします。まずは対策なしのまま押してみてください。
図1 — 罠サイトからのPOSTにもCookieは自動で付く。だから「ログイン済みか」だけでは本人の意思を確かめられない

認証は通っているのに、本人の意思ではない

CSRFの本質は、「認証(誰か)」は確認できているのに「意図(本人が望んだか)」を確認していないことにあります。ブラウザは bank.example 宛てのリクエストなら、それを引き起こしたのがどのサイトかに関係なく、保存済みのCookieを付けて送ります。サーバーから見ると、本人の操作と罠サイト経由の偽造リクエストがまったく同じ形で届く――だから区別できません。パスワードもトークンも盗まれていないのに成立するのが、CSRFの厄介なところです。

そこでCSRFトークンを使います。サーバーは正規のフォームを返すときに、セッションに紐づく推測できない使い捨ての値をhidden項目として埋め込み、POST時にそれが一致するかを検証します。罠サイトは他サイトのページ内容を読めない(同一オリジンポリシー)ので、この値を知りようがなく、偽造リクエストはトークン不一致で弾かれます。

もう1つの防波堤が SameSite 属性です。SameSite=Lax を付けたCookieは、他サイト発のPOSTにはそもそも送られません。近年のブラウザはこれを既定に近い扱いにしており、CSRFはかなり通りにくくなりました。ただし完全に置き換わるわけではないので、トークン検証と併用するのが定石です。加えて、状態を変える操作をGETで行わないことも重要な前提になります(<img src> だけで攻撃が成立してしまうため)。

用語ミニ辞書
CSRF
ログイン済みの利用者に、別サイト経由で意図しないリクエストを送らせる攻撃。
CSRFトークン
正規フォームに埋め込む使い捨ての値。他サイトからは読めないため偽造を防げる。
SameSite
Cookieの属性。Lax/Strict にすると他サイト発のリクエストにCookieが付かなくなる。
同一オリジンポリシー
別オリジンのページの中身をJSから読めない制限。トークンが守られる根拠。
XSSとの違い
XSSはスクリプトを注入して読み放題にする攻撃。CSRFは中身を読まずに操作だけさせる。

まとめ

CSRFはCookieが自動で付くという便利さの裏返しです。サーバーは「ログイン済みか」だけでなく「この操作を本人が意図したか」を確かめなければならない。その確認手段が、他サイトからは知りえないCSRFトークンと、他サイト発のリクエストにCookieを載せないSameSiteです。あわせて更新操作はPOST等に限る――この3点を押さえれば、図1の攻撃は成立しなくなります。