あなたの「ログイン中」を狙う見えない攻撃
銀行のATMで暗証番号を入力し終わった直後、あなたが画面から目を離した瞬間に、背後から誰かが勝手に「振込」ボタンを押してしまう――。CSRF(クロスサイト・リクエスト・フォージェリ)攻撃は、まさにこれと同じ原理のサイバー攻撃です。
パスワードを盗むのではなく、あなたが正規にログインしている状態そのものを悪用するのがCSRFの厄介な点です。ログイン中のブラウザに対して、攻撃者が用意した罠ページから「本人が送ったように見える」リクエストを送り込み、意図しない操作(送金、購入、設定変更など)を実行させます。
この記事では、CSRFの攻撃メカニズムから具体的な防御手法まで、実務で役立つレベルで解説します。
CSRF攻撃が成立する3つの条件
CSRF攻撃が成功するには、次の3つの条件がすべて揃う必要があります。逆にいえば、どれか1つでも潰せば攻撃は防げます。
条件1:ユーザーが対象サイトにログイン済み
ブラウザにセッションCookieが保存されており、サーバーがそのユーザーを「認証済み」と判断できる状態です。多くのWebサービスでは「ログインしたままにする」機能があるため、ユーザーが意識していなくても長期間この状態が維持されることがあります。
条件2:対象サイトがリクエストの出どころを検証していない
サーバー側が「このリクエストは本当に自サイトのフォームから送信されたのか」をチェックしていない場合、外部サイトからの偽リクエストと正規のリクエストを区別できません。
条件3:ユーザーが攻撃者の罠ページにアクセスする
攻撃者が仕込んだリンクやメール、広告バナーなどをユーザーがクリックし、罠ページを開くことがトリガーになります。罠ページには隠しフォームや自動送信スクリプトが埋め込まれており、ページを開いただけで攻撃が完了するケースもあります。
攻撃の流れをステップごとに追う
具体的なシナリオで攻撃の流れを見てみましょう。ここでは「ECサイトでの不正な住所変更」を例にします。
Step 1:被害者がECサイト(example-shop.com)にログインし、商品を閲覧中。ブラウザにはセッションCookieが保存されている。
Step 2:攻撃者は罠サイトを用意。ページ内に以下のような隠しフォームを設置する。
<form action="https://example-shop.com/account/address" method="POST">
<input type="hidden" name="address" value="攻撃者の住所">
</form>
<script>document.forms[0].submit();</script>
Step 3:被害者が罠サイトを開くと、ブラウザは自動的にECサイトへPOSTリクエストを送信。このとき、ブラウザはexample-shop.comのCookieを自動付与する。
Step 4:ECサイトのサーバーは正規のセッションCookieを確認し、「本人からのリクエスト」と判断。配送先住所が攻撃者の住所に書き換わる。
主要なCSRF対策手法とその仕組み
CSRF対策にはいくつかのアプローチがありますが、実務で最も採用されているのはCSRFトークンです。ただし、それぞれ長所と短所があるため、サービスの特性に応じた使い分けが重要です。
CSRFトークン(Synchronizer Token Pattern)
最も広く採用されている対策です。サーバーがフォーム表示時に一意の秘密トークンを生成し、hiddenフィールドとしてHTMLに埋め込みます。フォーム送信時にこのトークンをサーバーが検証し、一致しなければリクエストを拒否します。
攻撃者は被害者のトークン値を知ることができない(同一オリジンポリシーにより外部サイトからHTMLの中身は読めない)ため、正しいトークンを含むリクエストを偽造できません。
SameSite Cookie属性
Cookie自体に「別のサイトからのリクエストには付与しない」という制約を設定する方法です。SameSite=Laxに設定すると、外部サイトからのPOSTリクエストにはCookieが送信されなくなります。SameSite=StrictではGETも含むすべてのクロスサイトリクエストでCookieが送信されません。
モダンブラウザではデフォルトでSameSite=Laxが適用されるようになりましたが、すべてのブラウザ・バージョンで保証されるわけではないため、CSRFトークンとの併用が推奨されます。
Originヘッダ / Refererヘッダの検証
リクエストに含まれるOriginやRefererヘッダを確認し、自サイトからのリクエストかどうかを判定する方法です。実装がシンプルな反面、プロキシやブラウザ設定によってヘッダが送信されないケースがあり、単独での使用は推奨されません。
カスタムリクエストヘッダ(Double Submit Cookie)
AjaxリクエストでカスタムHTTPヘッダ(例:X-CSRF-Token)を付与する方法です。ブラウザの仕様により、クロスオリジンのリクエストにカスタムヘッダを付与するにはCORS設定が必要なため、攻撃者が外部サイトからカスタムヘッダ付きリクエストを送ることは困難です。SPAを中心としたAPIベースのアプリケーションで多く採用されています。
対策手法の比較表
| 対策手法 | 防御力 | 実装コスト | SPA対応 | 注意点 |
|---|---|---|---|---|
| CSRFトークン | 非常に高い | 中 | 要工夫 | サーバー側でトークン管理が必要 |
| SameSite Cookie | 高い | 低 | 対応 | 古いブラウザで非対応の場合あり |
| Origin/Referer検証 | 中程度 | 低 | 対応 | ヘッダが欠損するケースがある |
| カスタムヘッダ | 高い | 低 | 最適 | Ajax以外のフォーム送信には使えない |
フレームワーク別:CSRF対策の実装状況
主要なWebフレームワークは、デフォルトでCSRF対策機能を提供しています。ただし、「有効化されている」ことと「正しく動作している」ことは別問題です。実装時には必ず動作確認を行いましょう。
Ruby on Rails
protect_from_forgery with: :exceptionがデフォルトで有効です。フォームヘルパーを使うと自動的にCSRFトークンがhiddenフィールドに挿入されます。APIモードでは無効化されていることがあるため、必要に応じて手動で設定します。
Django
CsrfViewMiddlewareがデフォルトで有効です。テンプレート内で{% csrf_token %}を記述することでトークンが埋め込まれます。AjaxリクエストではCookieからトークンを読み取り、ヘッダに設定する必要があります。
Spring Security(Java)
Spring Securityを導入すると、CSRFプロテクションがデフォルトで有効になります。Thymeleafなどのテンプレートエンジンを使うと自動的にトークンが埋め込まれます。REST APIではステートレス認証(JWT等)と組み合わせる設計が多くなっています。
Laravel(PHP)
VerifyCsrfTokenミドルウェアがデフォルトで有効です。Bladeテンプレートで@csrfディレクティブを使うだけでトークンが挿入されます。API用のルート(api.php)ではデフォルトで無効化されているため、代替手段を検討します。
実務で陥りがちな落とし穴
CSRF対策を実装していても、以下のようなミスで防御が無効化されるケースがあります。開発・レビュー時にチェックしておきましょう。
トークンの検証をスキップしてしまう:特定のエンドポイントだけCSRF検証を除外した結果、重要な操作(パスワード変更など)が保護されなくなるケース。除外する場合は影響範囲を慎重に確認します。
GETリクエストで状態を変更する:データの削除や設定変更をGETリクエストで実装すると、imgタグやリンクだけで攻撃が成立します。状態変更は必ずPOST/PUT/DELETEで行うのがHTTPの原則です。
CORSの設定ミス:Access-Control-Allow-Origin: *を安易に設定すると、カスタムヘッダ方式の防御が弱体化します。許可するオリジンは明示的に指定しましょう。
サブドメインからの攻撃を見落とす:同一ドメインのサブドメインが攻撃者に制御された場合、SameSite Cookieでは防げないケースがあります。大規模サービスではサブドメイン間の信頼関係も考慮が必要です。
よくある質問(FAQ)
Q. CSRFとXSS(クロスサイト・スクリプティング)は何が違うのですか?
XSSは攻撃者のスクリプトを対象サイト上で実行させる攻撃で、CSRFは被害者のブラウザから対象サイトへ偽のリクエストを送信させる攻撃です。XSSは「サイトを信頼するユーザー」を、CSRFは「ユーザーを信頼するサイト」をそれぞれ悪用します。なお、XSS脆弱性があるとCSRFトークンを窃取される恐れがあるため、両方の対策が必要です。
Q. APIサーバー(REST API)にもCSRF対策は必要ですか?
Cookie認証を使わないステートレスなAPIサーバー(BearerトークンやAPIキーで認証)であれば、CSRFのリスクは基本的にありません。ただし、CookieベースのセッションでAPIを保護している場合はCSRF対策が必要です。
Q. SameSite Cookieだけで十分ですか?
現時点では単独での防御は推奨されません。古いブラウザ、サブドメイン攻撃、特殊なリダイレクトシナリオなどでバイパスされる可能性があります。CSRFトークンとの多層防御が最善策です。
Q. ログインフォーム自体にもCSRF対策は必要ですか?
はい。ログインCSRF(攻撃者のアカウントに被害者を強制ログインさせる攻撃)を防ぐため、ログインフォームにもCSRFトークンを設置すべきです。被害者が攻撃者のアカウントでログインした状態で入力した情報が、攻撃者に漏洩する恐れがあります。
まとめ:多層防御でCSRFリスクをゼロに近づける
CSRF対策のポイントを整理します。
第一の防御層:CSRFトークンをすべての状態変更リクエストに適用する。フレームワークのデフォルト機能を活用し、安易に除外しない。
第二の防御層:SameSite Cookie属性をLax以上に設定する。新規開発では最初から設定しておく。
第三の防御層:状態変更はPOST/PUT/DELETEメソッドに限定する。GETリクエストでデータを変更しない。
これらを組み合わせることで、CSRFによる被害を実質的にゼロに近づけることができます。セキュリティ対策はどれか1つに頼るのではなく、複数の層で守る「多層防御」の考え方が鍵です。

コメント