XSSという「終わらない悪夢」:ツール任せの診断が招く致命的な敗北
「DAST(動的アプリケーションセキュリティテスト)ツールでスキャンして、脆弱性ゼロです」。
現場でそんな報告を聞くと、私はいつも頭を抱えたくなる。ツールは確かに偉大だが、それは「最低限のガードレール」に過ぎない。特にXSS(クロスサイトスクリプティング)は、ブラウザの進化とフレームワークの複雑化に伴い、もはや単純な文字列マッチングでは検知できない領域に足を踏み入れている。
今日は、自動化ツールがなぜ「DOMベースのXSS」や「文脈依存の脆弱性」を見逃すのか、そして我々エンジニアがどうやってその穴を埋めるべきか、泥臭い現場の知見を共有しよう。
—
1. DASTツールの限界と「自動化の境界線」
自動化ツールは、URLのパラメータに javascript:alert(1) を放り込むような、古典的な「反射型XSS」の検知には長けている。しかし、現代のWebアプリケーションが抱える以下のリスクには無力に近い。
- DOMベースXSS: サーバー側にデータが送信されず、クライアントサイドのJavaScript内だけで完結する脆弱性は、外部スキャナからは「見えない」。
- 複雑なステート(状態): ログイン後のマルチステップフォームや、特定のユーザー権限でしか表示されない動的生成コンテンツは、ツールが巡回(クローリング)できない。
結論として、自動化は「広範囲のスクリーニング」に使い、手動テストは「ビジネスロジックの盲点」に使う。 これが鉄則だ。
—
2. 実戦:DOMベースXSSのPoCと回避術
DOMベースXSSは、攻撃者がURLのフラグメント(#以降)を操作して、悪意あるスクリプトをクライアント側で実行させる手法だ。
脆弱性のあるコード(ダメな例)
// URLのクエリパラメータから取得した値を、サニタイズせずにinnerHTMLにぶち込む
const params = new URLSearchParams(window.location.search);
const username = params.get(‘name’);
document.getElementById(‘welcome-msg’).innerHTML = “こんにちは、” + username + “さん”;
この実装に対し、攻撃者は ?name= というURLを生成し、被害者に踏ませるだけでセッションハイジャックが可能になる。
セキュアな実装コード
innerHTML は禁じ手だ。値を文字列としてのみ扱う textContent を使うか、DOMPurifyのようなライブラリで無害化するのが鉄則。
// 【推奨】textContentを使用する
const params = new URLSearchParams(window.location.search);
const username = params.get(‘name’);
// textContentはHTMLタグを解釈せず、ただの文字列としてレンダリングする
document.getElementById(‘welcome-msg’).textContent = “こんにちは、” + username + “さん”;
—
3. 防御の要:CSP(Content Security Policy)で「動かない」環境を作る
コードを完璧に書くのは難しい。だからこそ、多層防御が必要だ。たとえXSSの脆弱性が混入したとしても、「外部からのスクリプト実行を許さない」設定を行えば、被害は最小化できる。
NginxやWebサーバーのレスポンスヘッダーに、以下のCSPを設定することをお勧めする。
Nginx設定例:インラインスクリプトを禁止し、信頼できるドメインのみ許可
add_header Content-Security-Policy “default-src ‘self’; script-src ‘self’ https://trusted.cdn.com; object-src ‘none’;”;
default-src 'self': 基本的に自ドメイン以外のリソース読み込みを禁止。script-src 'self': インラインスクリプト()の実行を完全にブロックする。これがXSS対策の最強の盾だ。
—
4. 現場のシニアが教える「手動テスト」のチェックリスト
自動化ツールを回した後、手動で必ず確認すべきは以下の3点だ。
1. クライアントサイドのデータフロー: location.hash や localStorage から取得したデータが、DOMの更新に使われていないか?
2. JSライブラリのバージョン: 古いjQuery等を使っている場合、セレクタ操作に起因するXSS脆弱性がないか?([Snyk](https://snyk.io/)などで依存関係をチェックするのは必須)
3. WAFの過信: 「WAFが入っているから大丈夫」は思考停止だ。WAFはシグネチャベースの防御であり、ビジネスロジックに深く入り込んだ脆弱性には対応できない。
—
最後に:セキュリティは「完了」するものではない
XSS対策は、一度コードを修正して終わりではない。フロントエンドのフレームワークが更新されるたび、APIの仕様が変わるたび、新しい「攻撃の入り口」が生まれる。
「ツールに任せて安心」という安易な妥協を捨て、「どうすればこのデータがブラウザ上で実行可能なコードとして解釈されるか?」という視点を常に持ち続けてほしい。それが、プロのエンジニアとしてシステムを守り抜く唯一の道だ。
もし修正方針に迷ったら、いつでも相談してくれ。堅牢な設計は、泥臭い積み重ねの上にしか成り立たないのだから。
コメント