説明
ブラウザーのJavaScriptがユーザー入力を適切に処理せず、innerHTML、outerHTML、insertAdjacentHTML、document.write などのHTMLを解釈するAPIへ渡すと、DOMベースXSSが発生するおそれがあります。URLパラメーター、フラグメント、要素の値などからマークアップやイベントハンドラーを挿入される可能性があります。例えば、デコードされた ?q=<img src=x onerror=alert(1)> を innerHTML に入れると、JavaScriptが動作するおそれがあります。一度テキストとして扱った値でも、後からHTMLとして再解釈すると危険になります。
想定される影響
- ユーザーのブラウザーで任意のJavaScriptを実行されるおそれがあります。
- 読み取り可能なCookie、トークン、localStorage内のデータを盗まれ、アカウントを乗っ取られる可能性があります。
- DOMの操作によって画面の改ざん、入力の窃取、内部情報の露出が起こるおそれがあります。
- 偽のフォームやメッセージを使ったフィッシングにつながる可能性があります。
対処方法
- テキストには
textContentやinnerTextを使い、信頼できない値をHTML解釈用APIへ直接渡さないでください。 - マークアップが必要な場合は、DOMPurifyやsanitize-htmlなどの信頼できるサニタイザーで制限してください。
- HTML本文、属性、URL、JavaScriptのそれぞれに合う処理を適用し、異なる文脈へ流用しないでください。
- URLは
URLSearchParamsやnew URL()で解析し、必要に応じて許可値を検証してください。 - CSPや、対応する環境ではTrusted Typesを追加の防御として使ってください。
例
変更前
javascript
// 入力を処理せずにDOMへ出力
(function () {
// フラグメントをDOMへ挿入(実際に解釈される値を確認)
const raw = location.hash.slice(1); // 例: #<img src=x onerror=alert(1)>
const box = document.getElementById('msg');
// BAD: ユーザー入力をHTMLの文脈へ直接挿入
box.innerHTML = '<p>결과: ' + raw + '</p>';
// 別の危険: document.writeでユーザー入力を出力
const q = new URL(location.href).searchParams.get('q') || '';
document.write('<div>검색어: ' + q + '</div>');
})();
変更後
javascript
(function () {
const params = new URL(location.href).searchParams;
const q = params.get('q') || '';
const box = document.getElementById('msg');
// 1) HTMLとして解釈せずテキストを表示
box.textContent = `검색어: ${q}`;
// 2) HTMLが必要な場合はサニタイズ
// DOMPurifyをグローバルに読み込んでいる前提
const userHtml = params.get('html') || '';
const safeHtml = DOMPurify.sanitize(userHtml, {ALLOWED_TAGS: ['b','i','u']});
document.getElementById('preview').innerHTML = safeHtml;
// 3) URLなどの属性に合う形で値を組み立てる
const link = document.getElementById('link');
const dest = params.get('dest') || '/';
// 許可リストによる検証例
const allowed = ['/', '/help', '/profile'];
link.setAttribute('href', allowed.includes(dest) ? dest : '/');
})();
解説:
- 変更前: ユーザーが制御する値をHTML解釈用APIへ渡しています。
URLSearchParamsはクエリをデコードしますが、location.hashの読み取りはフラグメントをパーセントデコードしません。フラグメント内のエンコードされたタグだけで実行されると判断せず、実際のHTML文字列の生成経路を確認してください。 - 変更後:
textContentでテキストを表示し、許可するマークアップはDOMPurifyで制限します。URL属性もリスト内のパスだけを選びます。DOMPurifyと参照するページ要素は用意されている前提です。