はじめに
— 古くて新しい脆弱性が今も消えない理由
XSS(Cross-Site Scripting)は、ユーザーが信頼している Web サイトに、攻撃者のスクリプトが紛れ込んでしまう脆弱性だ。
「そんなの昔の問題じゃないの?」と思いたくなるが、実際には 今もバグバウンティや脆弱性レポートの常連。
なぜ現代のフレームワークや自動エスケープ機構があるのに、XSS はなくならないのか?
その本質的な原因を、実際の開発現場でよく起こるポイントとともに解説する。
1. 入力バリデーションとサニタイズの不足
Web アプリケーションはユーザーの入力を多用する。
- 検索フォーム
- コメント欄
- プロフィール編集
- メールアドレス/ユーザー名
- リッチテキスト入力
これらが適切にサニタイズされず、そのまま HTML に埋め込まれると、攻撃者はこうした payload を挿入できる:
<script>alert('XSS');</script>
これは“悪意のあるコード”ではなく ただの文字列 として扱われるべきなのに、それを正しく処理しなかった結果ブラウザに実行されてしまう。
つまり、
「信用してはいけないデータを信用しすぎている」
これが XSS の最も典型的な発生原因。
2. 出力エンコードのミス — 文脈ごとに必要な対策が違う
実は XSS 対策の核心は “文脈に応じたエンコード” だ。
HTML の中か?
JavaScript のリテラル内か?
URL パラメータか?
属性値か?
文脈ごとにエスケープする文字が違うのに、それを意識せず “雑にエスケープ” すると、穴が残る。
例:HTML では < > " ' & をエンコードすべきだが
JavaScript では \ " ' が問題になる。
出力エスケープは世界の掟だけど、文脈の違いを理解していないと破られる。
この「文脈のズレ」が、現代の XSS でも非常に多い原因のひとつ。
3. セキュリティヘッダー(CSP)が弱い・間違っている
Content-Security-Policy(CSP)は本来 XSS を強力に抑える仕組みだ。
しかし現実は……
-
unsafe-inlineの誤用 -
script-src *のような開き直った広範囲許可 - 不要な whitelist の氾濫
- CSP をそもそも設定していない
こうした“緩すぎる CSP”のせいで、本来ブロックされるはずの悪意スクリプトが普通に実行される。
CSP は強すぎると壊れる、弱すぎると守れない。
その微妙なバランスが難しい。
だから現場では「設定ミス → XSS 温存」が繰り返される。
4. フレームワークや言語仕様の問題
昔のフレームワークでは自動エスケープが弱く、XSS の温床になりがちだった。
現代では改善されているものの、以下の“例外ケース”がまだ危険:
- テンプレートエンジンで HTML を “安全なまま出力” するモード
- React/Vue の
v-html/dangerouslySetInnerHTML - Markdown パーサの脆弱性
- 言語ランタイムの XSS バグ(過去の jQuery / Angular など)
つまり、
フレームワークが安全でも、開発者がうっかり“安全を回避するモード”を使うと途端に危険になる。
5. サードパーティライブラリが脆弱だった
「アプリ本体は安全。でも依存ライブラリが爆発した」
これは現代 XSS の非常に多いパターン。
- UI ウィジェット
- Markdown / WYSIWYG エディタ
- テンプレートライブラリ
- サニタイズライブラリ自体の欠陥
開発者が触っていない部分で XSS が発生するため、気づく頃にはユーザー全体が影響を受けていることも。
依存が増えるほど、XSS 発生確率は上がる──悲しいけど現実。
まとめ
XSS の本質は:
-
ユーザー入力を信用してしまう
-
文脈ごとのエンコードが難しい
-
CSP を正しく扱うのが面倒
-
フレームワークの例外処理に罠がある
-
依存ライブラリが増え続けている
つまり、
“ちょっとした油断” が攻撃者に最高の隙を与える。
これが XSS が今も残り続ける最大の理由だ。