【実務・中級編】XSSによるCSRFトークン窃取と攻撃の連鎖 – アプリケーションセキュリティ & 安全な開発防御ガイド

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開発において最も警戒すべきリスクの一つだ。

コードを書くときは、「もしこの変数が攻撃者に書き換えられたらどうなるか?」という視点を常に忘れないでほしい。技術的な実装は、常に攻撃者の思考を先回りするものでなければならない。君たちが守っているのは単なるデータではない。ユーザーの信頼そのものだということを肝に銘じておいてくれ。

コメント

タイトルとURLをコピーしました