DevToolBox

HTMLエスケープとXSS対策の基本

最終更新日: 2026-06-11公開日: 2026-06-11執筆: DevToolBox編集部

「ユーザー名に <script> と入力されたらアラートが出た」—— XSS(クロスサイトスクリプティング)は、今も OWASP Top 10 に挙がり続ける Web 最頻出の脆弱性です。 対策の土台はシンプルで、ユーザー由来の文字列を HTML に埋め込むときに必ずエスケープすること。 ただし「どの5文字を」「どのコンテキストで」が曖昧なままだと漏れが生まれます。 本記事ではエスケープの基本から、React 時代の注意点、DOMPurify・CSP による多層防御までを整理します。

XSS remains one of the most common web vulnerabilities. The foundation is escaping user-supplied strings before inserting them into HTML — but which characters, and in which context, matters. This guide covers the 5 characters, context-aware escaping, React's auto-escaping and its escape hatches, DOMPurify, and CSP as defense-in-depth.

TL;DR

HTML Entity Encoder

文字列をHTMLエンティティに即座にエンコード/デコード。「この入力がエスケープ後どうなるか」をブラウザ内で確認できます。データ外部送信なし。

今すぐ試す →

1. エスケープすべき5文字 / The 5 characters

文字エンティティ放置した場合のリスク
&&amp;エンティティの誤解釈・二重エスケープの起点
<&lt;タグ開始 = <script><img onerror> の注入
>&gt;タグの終端操作
"&quot;属性値からの脱出(" onmouseover="...)
'&#39;シングルクォート属性からの脱出
// 最小実装(順序が重要: & を最初に)
function escapeHtml(s) {
  return s
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");
}

& の変換を最後にすると、変換済みの &lt;&amp;lt; になる二重エスケープが起きます。順序は & が先です。 実際の攻撃でよく使われるのは <script> よりも<img src=x onerror="fetch(...)"> のようなイベントハンドラ属性で、< のエスケープだけでこの注入は無効化できます。

Escape & first or you will double-escape. Real-world payloads favor event handlers like img onerror over script tags — escaping < neutralizes them.

2. コンテキストが変わればルールも変わる / Context-aware escaping

HTMLエスケープは「HTML本文・引用符付き属性」向けのルールです。 別のコンテキストに同じ文字列を置くなら、別のエンコードが必要になります。

埋め込み先必要な処理典型的な失敗
HTML本文HTMLエスケープ(5文字)innerHTML に未処理文字列
属性値HTMLエスケープ + 必ず引用符で囲む引用符なし属性(value= 区切りで属性追加し放題)
URL(クエリ等)encodeURIComponentjavascript:alert(1) を href にそのまま
JS文字列内JSON.stringify で埋め込み</script> 文字列でスクリプト終端を突破される

特に href / src は要注意です。エスケープしてもjavascript: スキームは文字として正当なので素通りします。 URL を受け取る場所では「http:/https: のみ許可」というスキームの許可リスト検証を入れてください。

Escaping rules depend on context: attribute values need quotes, URLs need encodeURIComponent plus a scheme allowlist (javascript: passes HTML escaping untouched), and strings inside scripts should be embedded via JSON.stringify.

3. React / モダンフレームワークでの注意点 / React and friends

// 安全: JSX の {} は自動エスケープされる
<p>{userInput}</p>

// 危険: HTML として解釈される
<div dangerouslySetInnerHTML={{ __html: userInput }} />

// 危険: スキームは検証されない(React 19 は javascript: を警告/ブロックするが過信しない)
<a href={userInput}>link</a>

// HTML を描画したいなら sanitizer を通す
import DOMPurify from "dompurify";
<div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(userHtml) }} />

JSX interpolation is safe by default. The escape hatches — dangerouslySetInnerHTML, raw hrefs, permissive Markdown renderers — are where XSS re-enters. Sanitize with DOMPurify when you must render HTML.

4. 多層防御: CSP / Defense in depth with CSP

エスケープが第一の防御なら、Content-Security-Policy は「漏れたときに実行させない」第二の防御です。

# インラインスクリプトを禁止し、自ドメイン + nonce のみ許可
Content-Security-Policy: script-src 'self' 'nonce-R4nd0m';
                         object-src 'none';
                         base-uri 'none';

'unsafe-inline' を許可した CSP は XSS 防御としてはほぼ無力です。 nonce(リクエストごとのランダム値)または hash ベースで運用し、 まずは Content-Security-Policy-Report-Only で影響を観測してから強制に切り替えるのが 現実的な導入手順です。

A nonce- or hash-based script-src (never unsafe-inline) limits damage when an escaping bug ships. Roll out with Report-Only first.

まとめ

XSS対策の優先順位は「(1) 出力時に必ずエスケープ(コンテキスト別)→ (2) HTML描画はサニタイザ経由 → (3) CSPで保険」。 フレームワークの自動エスケープに乗りつつ、dangerouslySetInnerHTML と URL だけは 常に疑う——これだけで大半の XSS は防げます。 エスケープ結果の確認には HTML Entity Encoder を、エスケープ済みHTMLの整形確認にはHTML Formatter をどうぞ。

Escape on output per context, sanitize any HTML you must render, and back it all with a strict CSP. Stay suspicious of dangerouslySetInnerHTML and user-supplied URLs.

HTML Entity Encoder

攻撃文字列がエスケープ後どう無害化されるかを実際に確認。エンコード/デコード両対応。

今すぐ試す →

よくある質問 / FAQ

HTMLでエスケープすべき文字はどれ?
最低限 & < > " ' の5文字です。& は &amp;、< は &lt;、> は &gt;、" は &quot;、' は &#39;(または &apos;)に変換します。& を最初に変換しないと二重エスケープが発生する点に注意してください。
ReactならXSS対策は不要?
JSXの {} 埋め込みは自動エスケープされるため基本は安全ですが、dangerouslySetInnerHTML、href への javascript: URL、ユーザー入力をそのまま使う style や属性スプレッドは依然として危険です。HTML文字列を描画する必要がある場合は DOMPurify 等でサニタイズしてください。
textContent と innerHTML はどちらを使うべき?
ユーザー入力を表示するなら textContent(または createTextNode)一択です。innerHTML は文字列をHTMLとして解釈するため、入力に <img onerror=...> 等が含まれると実行されます。
Which characters must be escaped to prevent XSS?
At minimum & < > " ' — mapped to &amp; &lt; &gt; &quot; &#39;. Escape & first to avoid double-escaping. Escaping alone is not sufficient for attribute, URL, or JavaScript contexts: each context needs its own encoding rules.
Is escaping enough, or do I need CSP too?
Escaping is the primary defense; Content-Security-Policy is defense-in-depth. A strict CSP (no unsafe-inline, nonce-based scripts) limits the blast radius when an escaping bug slips through.

関連ガイド・ツール / Related