はじめに:なぜ「XHR × CSRF」が重要なのか
かつて CSRF は、
-
<form>の自動送信 -
<img src=...>による GET リクエスト
といった 「古典的な攻撃」 という印象がありました。
しかし現代の Web アプリは、
- SPA(Single Page Application)
- 設定変更・操作を ページ遷移なし
- API 通信は XMLHttpRequest / Fetch
が当たり前です。
CSRF もまた「非同期 API 攻撃」に進化しています。
1. XMLHttpRequest(XHR)とは何か
XMLHttpRequest は、JavaScript から HTTP リクエストを送るための API です。
const xhr = new XMLHttpRequest();
xhr.open("POST", "/api/updateProfile");
xhr.send("name=alice");
特徴:
- ページ遷移なし
- バックグラウンド通信
- Cookie を 自動送信(条件次第)
XHR 自体はセキュリティ機構ではありません
2. CSRF の本質(XHR でも変わらない)
CSRF(Cross-Site Request Forgery)の本質はシンプルです。
サーバが「Cookie がある=本人操作」と誤認すること
攻撃者は:
- Cookie を盗まない
- 認証を突破しない
代わりに:
被害者のブラウザを“代理人”として使う
3. なぜ XHR でも CSRF が成立するのか
核心ポイント
ブラウザは XHR でも Cookie を自動で付与する
POST /api/updateEmail
Cookie: session=abcd1234
- 攻撃者は Cookie を知らない
- だが 被害者のブラウザが勝手に付ける
つまり:
フォーム送信でも XHR でも、本質は同じ
4. 非同期 CSRF(Asynchronous CSRF)とは
非同期 CSRF とは:
ページ遷移を伴わず、
JavaScript の非同期通信(XHR / Fetch)で成立する CSRF
特徴:
- ユーザーが気づかない
- UI 変化がない
- バックグラウンドで設定が改変される
5. 攻撃シナリオ:Web メール設定の改ざん
想定アプリ
-
Web メール:
mailbox.thm -
API:
POST /api/updateEmail
Step 1:被害者がログイン
Victim → mailbox.thm
-
Cookie にセッション保存
session=abcd1234
Step 2:攻撃者サイトへ誘導
Victim → attacker.example
- フィッシング
- SNS リンク
- 広告
Step 3:悪意ある JavaScript が XHR を実行
const xhr = new XMLHttpRequest();
xhr.open("POST", "https://mailbox.thm/api/updateEmail", true);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify({
forwardTo: "attacker@mail.example"
}));
Step 4:ブラウザが Cookie を自動付与
POST /api/updateEmail
Host: mailbox.thm
Cookie: session=abcd1234
Content-Type: application/json
ここが CSRF の成立点
Step 5:サーバが本人操作だと誤認
✔ セッション Cookie が有効
✘ CSRF トークン検証なし
➡ 被害者のメール転送設定が変更される
※ レスポンスが読めなくても 攻撃は成功
6. CORS があっても防げない理由
よくある誤解:
「クロスオリジンだから CORS で防げる」
事実
- CORS は レスポンスを読めるかの制御
- CSRF は 送信できるだけで成立
| 観点 | CORS | CSRF |
|---|---|---|
| 防ぐ対象 | 情報漏えい | 不正操作 |
| リクエスト送信 | ❌ 防げない | — |
| Cookie 送信 | ❌ 防げない | 核心 |
CORS ≠ CSRF 対策
7. なぜ非同期 CSRF は特に危険か
① 視覚的変化がない
- ページ遷移なし
- 確認画面なし
② API が油断して作られがち
if (isAuthenticated) {
updateSettings();
}
③ SPA では API 数が多い
- 1 つの漏れが致命傷
8. 防御設計:XHR 前提の正解パターン
① SameSite Cookie(第一防波堤)
Set-Cookie: session=abcd;
Secure;
HttpOnly;
SameSite=Lax
- クロスサイト送信を抑制
- ただし 単独では不十分
② CSRF トークン(必須)
フロントエンド
xhr.setRequestHeader("X-CSRF-Token", csrfToken);
サーバ
- Cookie のセッションと紐付く token を検証
- 一致しなければ拒否
Cookie だけでは操作不可にする
③ Origin / Referer 検証(補助)
Origin: https://mailbox.thm
- 正規オリジン以外を拒否
- SameSite=None 構成では特に重要
9. セキュリティテスト観点
- 重要操作が GET でない
- Cookie に SameSite が設定されている
- XHR / Fetch で CSRF token 必須
- Origin / Referer を検証
- CORS に credentials の誤設定がない
- レスポンスに依存せず副作用が起きないか検証
まとめ
XMLHttpRequest は CSRF を防がない。
非同期になっても、CSRF の本質は「Cookie を信じすぎる設計」にある。