XSSとは
クロスサイトスクリプティング(XSS)は、WebサイトがユーザーのブラウザでHTMLドキュメントをレンダリングする過程で、予期せぬスクリプトが実行されてしまう脆弱性です。最も単純な例として、<script>alert(1)</script>が挙げられます。
XSSの種類
反射型XSS
攻撃者が作成した悪意のあるURLをユーザーがクリックすることで、ユーザーのブラウザ上でスクリプトが実行されるタイプのXSSです。例えば、検索パラメータにスクリプトを埋め込み、その結果がページに反映されるケースが該当します。
以下のようなURLが考えられます:
https://example.com/search?query=<script>alert('XSS')</script>
格納型XSS
悪意のあるスクリプトがデータベースやファイルに保存され、他のユーザーがそのページを閲覧した際にスクリプトが実行されるタイプのXSSです。掲示板やコメント機能など、ユーザーが入力した内容が保存され、他のユーザーにも表示される機能でよく見られます。
攻撃の流れ:
- 攻撃者が悪意のあるコードを含むデータをWebサイトに送信
- Webサイトがコードをデータベースに保存
- 他のユーザーがページを要求
- サーバーが悪意のあるコードを含むデータをクライアントに送信
- ユーザーのブラウザでコードが実行され、攻撃が完了
DOMベースXSS
ドキュメントオブジェクトモデル(DOM)の操作を介して発生するXSSです。サーバー側ではなくクライアント側でDOMが操作される際に、適切な検証が行われていない場合に発生します。
以下に脆弱性のあるコード例を示します:
<script>
function updateContent() {
var userInput = document.getElementById("userInput").value;
document.getElementById("displayArea").innerHTML = "<a href='" + userInput + "'>リンク</a>";
}
</script>
<div id="displayArea"></div>
<input type="text" id="userInput" value="" />
<input type="button" value="更新" onclick="updateContent()" />
このコードでは、ユーザー入力が直接HTMLに挿入されるため、以下のような入力でXSSが発生します:
' onclick=alert('XSS攻撃') //
これにより、生成されるHTMLは次のようになり、リンクをクリックするとスクリプトが実行されます:
<a href='' onclick=alert('XSS攻撃') //'>リンク</a>
XSS防御策
入力フィルタリング
ユーザーからの入力データを厳密に検証し、許可された形式や長さのデータのみを受け付けます。具体的には:
- データ型の検証:年齢は数値のみ、名前は英数字のみなど
- 危険なHTMLタグのフィルタリング:
<script>、<iframe>など - JavaScriptイベントハンドラのフィルタリング:
onclick、onerror、onfocusなど
HTMLエンティティ化
特殊文字をHTMLエンティティに変換することで、スクリプトとして解釈されるのを防ぎます。
| 表示結果 | 説明 | エンティティ |
|---|---|---|
| < | 小なり | < |
| > | 大なり | > |
| & | アンパサンド | & |
| " | 二重引用符 | " |
| ' | 単一引用符 | ' |
エンコーディング
1. HTMLEncode:特殊文字をHTMLエンティティに変換(&、<、>、"、'、/など)
2. JavaScriptEncode:バックスラッシュ(\)を使用して特殊文字をエスケープ
防御関数の利用
// HttpOnly属性を設定してJavaScriptからのCookieアクセスを防止
setcookie("session_id", "value", time()+3600, "/", "", true, true);
// HTMLタグをエンティティに変換
htmlspecialchars($user_input, ENT_QUOTES, 'UTF-8');
// HTMLタグを除去
strip_tags($user_input);
// JSONコンテンツのエスケープ
function escapeEmbedJSON($data) {
return json_encode($data, JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP);
}
X-XSS-Protectionヘッダの設定
レスポンスヘッダにX-XSS-Protectionを設定することで、ブラウザ組み込みのXSSフィルタを有効にできます。
X-XSS-Protection: 0- XSS保護を無効化X-XSS-Protection: 1- XSS保護を有効化(デフォルト)X-XSS-Protection: 1; mode=block- XSS攻撃を検出した場合にページのレンダリングを停止
URLエンコーディング
信頼できないデータをURLパラメータとして使用する場合、encodeURIComponentでエンコードします。
function encodeURLParam(input) {
return encodeURIComponent(input);
};
CSSエンコーディング
信頼できないデータをCSSとして使用する場合、特殊文字をエスケープします。
function encodeCSS(input) {
let encoded = '';
for (let i = 0; i < input.length; i++) {
let char = input.charAt(i);
if (!/[a-zA-Z0-9]/.test(char)) {
let hex = input.charCodeAt(i).toString(16);
let padding = '000000'.substr(hex.length);
encoded += '\\' + padding + hex;
} else {
encoded += char;
}
}
return encoded;
};
JavaScriptエンコーディング
onclick、onerror、href、srcなどの属性ではHTMLエンコーディングだけでは不十分です。JavaScriptエンコーディングも併用する必要があります。
コンテンツセキュリティポリシー(CSP)
Content-Security-Policyヘッダを使用することで、許可されたリソースのみを読み込むようにブラウザに指示できます。
<meta http-equiv="Content-Security-Policy" content="
default-src 'self';
script-src 'self' 'unsafe-inline' https://trusted.cdn.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https:;
connect-src 'self';
frame-src 'self';
">
主なディレクティブ:
default-src- デフォルトのポリシー設定script-src- JavaScriptのソース制限style-src- スタイルシートのソース制限img-src- 画像のソース制限connect-src- AJAX、WebSocketなどの接続先制限frame-src- フレームのソース制限
一般的なペイロード構築手法
タグのネストによるフィルタリング回避
<scr<script>ipt>alert('XSS')</scr</script>ipt>
属性値のクロージング
"><script>alert('XSS')</script>
JavaScriptイベントハンドラの利用
<img src=x onerror=alert('XSS')>
<div onmouseover="alert('XSS')">マウスを乗せて</div>
iframeタグの利用
<iframe src="javascript:alert('XSS')"></iframe>
フォームアクションの悪用
<form action="javascript:alert('XSS')"><input type=submit></form>
大文字・小文字の混在による回避
<ScRiPt>alert('XSS')</sCrIpT>
二重記述による回避
<scrscriptipt>alert('XSS')</scrscriptipt>
エンコーディングによる回避
javascrip%0at:alert('XSS')
javascrip%09t:alert('XSS') // タブ文字
javascrip%0at:alert('XSS')//http://
javascrip%0at:alert('XSS')/*http://*/
ヌルバイトの挿入
<scri%00pt>alert('XSS');</scri%00pt>
<scri\x00pt>alert('XSS');</scri%00pt>
src属性の悪用
<img src=x onerror=prompt('XSS');>
<svg onload=alert('XSS')>