XSSは「単なる迷惑」ではない:CSRFトークン窃取による攻撃の連鎖を断つ
現場でよく耳にするのが「XSS(クロスサイトスクリプティング)は、せいぜいアラートが出るくらいでしょ?」という誤解だ。断言する。それは、あなたが防衛側の視点しか持っていない証拠だ。
攻撃者にとって、XSSは単なる「お遊び」の入り口ではない。本丸であるCSRF(クロスサイトリクエストフォージェリ)対策を無効化するための「鍵開けツール」なのだ。今回は、XSSを足がかりにCSRFトークンを強奪し、ユーザーになりすまして機密操作を行う、実戦的な攻撃シナリオとその防御策を紐解いていこう。
—
1. 攻撃者が狙う「盲点」:なぜトークンは盗まれるのか
通常、CSRF攻撃は「トークンを知らない攻撃者が、無理やりリクエストを投げる」という手法をとる。しかし、同一オリジンポリシー(SOP)を突破できるXSSが存在すれば、話は別だ。
攻撃シナリオのリアル
1. XSS発火: 攻撃者が仕込んだスクリプトが、被害者のブラウザ上で実行される。
2. トークン強奪: スクリプトがDOMを走査し、HTML内のからトークン文字列を読み取る。
3. リクエスト偽造: 盗んだトークンを付与した「正規のリクエスト」を、JavaScriptの fetch や XMLHttpRequest を使ってバックエンドへ送信する。
バックエンドから見れば、これは「正規のトークン」を持ったリクエストだ。CSRF対策は完全に「ザル」となり、パスワード変更や決済操作がいとも簡単に実行される。
—
2. セキュアな実装:防御の要は「属性の制約」にある
この攻撃を防ぐには、XSSを封じるのはもちろんのこと、「仮にXSSが埋め込まれてもトークンを盗ませない」という多層防御が不可欠だ。
防御の鉄則:HttpOnlyとSameSite属性
まず、トークンをJavaScriptから絶対に触らせないこと。そして、Cookieの設計を見直すことだ。
PHPでのセキュアなセッション・トークン管理例
PHPでCSRFトークンを実装する際、トークンをHTMLに埋め込むのではなく、「SameSite=Strict」属性を付与したCookieに保存し、バックエンドで照合するのが最も堅牢だ。
0,
‘path’ => ‘/’,
‘domain’ => ‘yourdomain.com’,
‘secure’ => true, // HTTPS必須
‘httponly’ => true, // JavaScriptからのアクセスを禁止(XSS対策の要)
‘samesite’ => ‘Strict’ // CSRFの根本対策
]);
session_start();
// CSRFトークンの生成とクッキーへの保存
if (empty($_SESSION[‘csrf_token’])) {
$_SESSION[‘csrf_token’] = bin2hex(random_bytes(32));
}
// フォームへ埋め込むのではなく、ヘッダーやカスタムヘッダーでやり取りする設計が理想
?>
フロントエンド:JavaScriptからの攻撃を防ぐ(Content Security Policy)
万が一、XSS脆弱性が残っていたとしても、CSP(Content Security Policy)を適切に設定していれば、外部サーバーへのトークン送信を物理的にブロックできる。
Nginxの設定例:
信頼できないインラインスクリプトを禁止し、データの送信先を制限する
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’; connect-src ‘self’ https://api.yourdomain.com; object-src ‘none’;”;
default-src 'self': 外部からのリソース読み込みを原則禁止。connect-src 'self':fetchなどでデータを送信できる先を自ドメインに限定(これにより、攻撃者のサーバーへトークンを送信する行為をブロック)。
—
3. 実務的な「泥臭い」インシデント対策Tips
教科書的な対策だけでなく、現場で生き残るための知見を共有する。
- DOM構造に依存しない実装: フォームの隠しフィールドにトークンを置く手法は、XSSによるDOM操作で容易に取得される。可能な限り、トークンはHTTPリクエストヘッダー(例:
X-CSRF-Token)に入れて送信させ、フロントエンド側ではJS変数のメモリ上にのみ保持する設計に倒すこと。 - 「とりあえず全ページでCSP」: 開発中のアプリであっても、CSPを
Report-Onlyモードで運用し、違反レポートをログに流し続けること。どのページでどのスクリプトが動いているかを把握するだけで、脆弱性が見つかる確率が劇的に上がる。 - WAFは最後の砦: AWS WAFなどを利用している場合、
Cross-Site Scriptingのルールセットを有効にするのは当然だが、カスタムルールで特定の機密エンドポイントへのPOSTリクエストにおいて、RefererやOriginヘッダーを厳格にチェックしておくこと。これはXSSを許してしまった際の最後の防波堤になる。
最後に:セキュリティは「性悪説」で設計せよ
「自分の書いたコードは安全だ」という思い込みが、最大の脆弱性を生む。XSSを許容した瞬間にCSRF対策が崩壊するこの攻撃手法は、現代のWeb開発において最も警戒すべきリスクの一つだ。
コードを書くときは、「もしこの変数が攻撃者に書き換えられたらどうなるか?」という視点を常に忘れないでほしい。技術的な実装は、常に攻撃者の思考を先回りするものでなければならない。君たちが守っているのは単なるデータではない。ユーザーの信頼そのものだということを肝に銘じておいてくれ。
コメント