XSS(クロスサイトスクリプティング)は、悪意あるスクリプトが正規のWebページに注入され、利用者のブラウザで実行される脆弱性です。この攻撃によりセッションハイジャックや個人情報の不正取得が発生するため、フロントエンド開発者は適切な対策を実装する必要があります。本稿では実践的な防御手法をコード例を交えて解説します。
XSSは主に3種類に分類されます。サーバーに悪意あるスクリプトが永続化される「永続型」、URLパラメータ経由で一時的に注入される「反射型」、クライアントサイドのDOM操作で発生する「DOM型」です。特にDOM型はサーバー経由しないため、フロントエンド単体での対策が必須です。
入力データのサニタイジング
ユーザ入力の検証には、単純な文字列置換ではなくDOM操作を活用した手法が有効です。以下の例では、サニタイズ処理を専用クラスで実装し、Unicode制御文字の除去も追加しています。
表示時のエンコーディングは、出力先に応じて処理を分けることが重要です。HTMLコンテキストでは要素のtextContentを使用し、JavaScript埋め込み時にはJSONシリアライズを適用します。
HTTPヘッダーによる防御策として、CSPの設定が有効です。以下の例ではスクリプトソースを厳密に制限し、インラインスクリプトの実行を禁止しています。
Vue.jsではv-htmlの利用に際し、DOMPurifyによる事前処理が必須です。以下は安全な実装例です。
ユーザ入力の検証には、単純な文字列置換ではなくDOM操作を活用した手法が有効です。以下の例では、サニタイズ処理を専用クラスで実装し、Unicode制御文字の除去も追加しています。
class InputSanitizer {
static clean(input) {
if (typeof input !== 'string') throw new TypeError('文字列のみ許可');
const maxLength = 200;
if (input.length > maxLength) throw new Error('入力が長すぎます');
const tempDiv = document.createElement('div');
tempDiv.textContent = input;
let sanitized = tempDiv.innerHTML;
// 制御文字の除去
sanitized = sanitized.replace(/[\u0000-\u001F\u007F-\u009F]/g, '');
return sanitized.substring(0, maxLength);
}
}
出力時のエンコーディング戦略表示時のエンコーディングは、出力先に応じて処理を分けることが重要です。HTMLコンテキストでは要素のtextContentを使用し、JavaScript埋め込み時にはJSONシリアライズを適用します。
// HTML出力用
function escapeHtml(content) {
const element = document.createElement('span');
element.textContent = content;
return element.innerHTML;
}
// JavaScript文字列埋め込み用
const safeScript = `const userInput = ${JSON.stringify(rawData)};`;
Content-Security-Policyの実装HTTPヘッダーによる防御策として、CSPの設定が有効です。以下の例ではスクリプトソースを厳密に制限し、インラインスクリプトの実行を禁止しています。
Content-Security-Policy:
default-src 'self';
script-src 'self' https://trusted.cdn.example.com;
style-src 'self' 'unsafe-inline';
object-src 'none';
フレームワーク別の安全な実装パターンVue.jsではv-htmlの利用に際し、DOMPurifyによる事前処理が必須です。以下は安全な実装例です。
<template>
<div v-html="safeContent"></div>
</template>
<script>
import DOMPurify from 'dompurify';
export default {
computed: {
safeContent() {
return DOMPurify.sanitize(this.userInput, {
ALLOWED_TAGS: ['b', 'i', 'em'],
FORBID_ATTR: ['style']
});
}
}
}
</script>
ReactではJSXの仕組みにより自動エスケープが適用されますが、dangerouslySetInnerHTMLの使用は避け、必要に応じてサニタイジングライブラリと組み合わせます。Angularの場合はバインディング構文で自動的にエスケープされるため、innerHtmlの直接操作を避けることが重要です。
CSPのreport-uriディレクティブを設定することで、違反イベントを収集し継続的なセキュリティ改善に活用できます。また、サードパーティスクリプトの導入時には信頼性を厳密に確認する必要があります。