HTMLエスケープとXSS — なぜ特殊文字を変換するのか

HTMLエスケープは、& < > " ' といった「HTMLとして特別な意味を持つ文字」を、表示上は同じに見えるエンティティ(文字参照)に置き換える処理です。これを怠ると、ユーザーが入力した文字列がそのままタグやスクリプトとして解釈され、XSS(クロスサイトスクリプティング)という深刻な脆弱性につながります。本記事では、何を・なぜ変換するのか、XSS の仕組みと種類、そして出力コンテキストに応じた正しい防ぎ方までを正確に整理します。

結論を先に:「ユーザー由来の値を画面に出すときは、出力する場所(コンテキスト)に合わせてエスケープする」がXSS対策の核心です。入力時の検証だけでは不十分で、HTML本文・属性・JavaScript・URL では必要なエスケープが異なります。現代のフレームワークは既定で自動エスケープしてくれるため、まずはそれを無効化しないことが第一歩です。

1. HTMLエスケープとは — 5つの特殊文字をエンティティに

ブラウザは HTML を読むとき、特定の文字を「構造を表す記号」として扱います。たとえば < は「ここからタグが始まる」という合図で、& は「ここから文字参照が始まる」という合図です。これらの文字を文字そのものとして表示したいときは、エンティティに置き換える必要があります。これが HTMLエスケープです。

HTMLエスケープで対象になる代表的な文字は次の5つです。コードや記号はすべてエンティティ表記で示します。

文字名前エンティティ(名前付き)数値文字参照
&アンパサンド&amp;&#38;
<小なり&lt;&#60;
>大なり&gt;&#62;
"ダブルクォート&quot;&#34;
'シングルクォート&#39;(または &apos;&#39;
変換の順序に注意:必ず最初に &&amp; に変換します。先に <&lt; に変えてから & を変換すると、いま生成した & まで二重に変換され(&amp;lt; のように)壊れてしまうためです。

たとえば、ユーザーが <b>太字</b> と入力したとします。これを エスケープせずに出力すると、ブラウザは <b> をタグとみなして「太字」を実際に太字で表示します。エスケープして &lt;b&gt;太字&lt;/b&gt; として出力すれば、画面には文字どおり <b>太字</b> という文字列が表示されます。

2. なぜ必要か — ユーザー入力がHTMLとして解釈される危険

Webアプリの多くは、ユーザーが入力した値(名前・コメント・検索キーワードなど)を画面に表示します。問題は、その値がそのまま HTML に埋め込まれる場合です。ブラウザは「これはデータ」「これは命令(タグ)」を区別できず、埋め込まれた文字列をHTMLの一部として解釈してしまいます。

仮にコメント欄へ次のような文字列が投稿されたとします(エンティティ表記で示します)。

<script>alert(document.cookie)</script>

これをエスケープせずページに出力すると、ブラウザは <script> を本物のスクリプトタグとして実行します。結果として、そのページを開いた他のユーザーのブラウザ上で、攻撃者の用意したコードが動いてしまうのです。これがXSSの基本構図です。

ポイントは「データと命令が同じ通り道(HTML)を流れている」ことです。エスケープは、データを命令と取り違えられないよう無害な文字列として明示する仕組みだと考えると分かりやすいでしょう。SQLにおけるプレースホルダ(プリペアドステートメント)と同じ発想です。

3. XSS(クロスサイトスクリプティング)の仕組みと種類

XSS は、攻撃者の文字列がページ内でスクリプトやHTMLとして実行される脆弱性です。実行されると、Cookieやアクセストークンの窃取、画面の改ざん、フォームの偽装、利用者になりすました操作などが可能になります。XSSは入り込み方によって大きく3種類に分けられます。

種類攻撃文字列の経路特徴
反射型(Reflected)URLパラメータ等で送られ、応答にそのまま反射される罠リンクを踏ませる必要がある。検索結果やエラー表示で起きやすい
格納型(Stored)DB等に保存され、後で他ユーザーに配信される掲示板やコメント欄で発生。閲覧者全員に影響が及び被害が大きい
DOM型(DOM-based)サーバを介さず、ブラウザ内のJavaScriptが危険な処理に渡すinnerHTMLlocation 等の扱いが原因。サーバ側のエスケープだけでは防げない

反射型と格納型はサーバが文字列を出力する瞬間のエスケープ漏れが原因です。DOM型は、サーバから来たHTMLは正しくても、ブラウザ内のJavaScriptがユーザー由来の値を innerHTML などへ無加工で渡すことで発生します。つまり「サーバ側だけ直せば安全」ではない点に注意が必要です。

4. エスケープで防ぐ — 出力コンテキストに応じた対策

XSS対策の本質は「値を出力する場所に合わせてエスケープする」ことです。同じユーザー入力でも、HTML本文に置くのか、属性値に置くのか、JavaScriptの中に置くのかで、危険な文字も必要なエスケープも変わります。

HTML本文(要素の中身)

最低限 & < > を変換します。これでタグの開始を封じられます。

属性値(attribute)

属性は必ずクォートで囲み、本文の3文字に加えて、囲んでいるクォート(" または ')を変換します。クォートをエスケープしないと、"><script>... のように属性を抜け出して新しいタグを差し込まれます。たとえば値が x" onmouseover="alert(1) のような場合、属性を脱出してイベントハンドラを注入されてしまいます。

JavaScript文脈

ユーザー入力を <script> 内に直接埋め込むのは避けるのが原則です。どうしても渡す場合は、HTMLエスケープではなくJavaScriptとして安全な形(たとえばサーバ側で JSON.stringify 相当のエンコードを行い、加えて </script> 等を無害化)にします。HTML用のエスケープをそのまま流用しても安全にはなりません。

URL文脈

hrefsrc にユーザー値を入れる場合は、URLエンコードに加えてスキームの検証が必要です。javascript: から始まるURLはクリックでスクリプトを実行するため、http / https など許可したスキームだけを通します。

入力検証とエスケープは別物:入力時のバリデーション(形式チェック)は有用ですが、それ単独ではXSSを防げません。出力時のエスケープこそが直接の防御策です。両者を併用し、出力エスケープを「最後の砦」として必ず通すのが鉄則です。

5. エンティティの種類 — 名前付きと数値文字参照

エスケープ後の表現である「文字参照」には2系統あります。どちらも表示結果は同じ文字になります。

いずれの形式も末尾のセミコロン ; を付けるのが正しい書き方です。なお &apos;(シングルクォートの名前付き)は HTML では広く使えますが、互換性の都合から実務では数値の &#39; が好まれることがあります。エスケープを自分で実装するより、言語やフレームワークの標準関数を使うのが安全かつ確実です。

数値文字参照は、ブラウザが解釈する段階で元の文字に戻ります。そのため攻撃者は &#60;script&#62; のような表現で検出をすり抜けようとすることがあります。独自のブラックリスト(特定の文字列を弾く方式)でXSSを防ごうとしないでください。正攻法は、出力時に必ずエスケープ(またはサニタイズライブラリ)を通すことです。

6. 実務 — フレームワークの自動エスケープとCSP

現代のテンプレートエンジンやUIフレームワーク(React、Vue、各種サーバサイドテンプレート等)は、変数を埋め込むと既定でHTMLエスケープします。多くのXSSは、この自動エスケープを「生のHTMLを挿入する機能」で明示的に無効化したときに発生します。生HTML挿入の機能(innerHTML 相当)は、信頼できない値には使わないことが大原則です。

まとめ:XSS対策は「出力コンテキストに応じたエスケープ」を土台に、フレームワークの自動エスケープを無効化しないこと、信頼できない値を生HTMLとして挿入しないこと、そしてCSPで多層防御を組むこと、の積み重ねです。
Free Tool HTML Escape ツールで実際に変換する ブラウザ内で文字列を HTMLエスケープ/アンエスケープできます。特殊文字がどのエンティティに変換されるかをその場で確認できます。

よくある質問(FAQ)

HTMLエスケープとは何ですか?

HTMLとして特別な意味を持つ文字を、表示上は同じに見えるエンティティ(文字参照)に置き換える処理です。具体的にはアンパサンド、小なり、大なり、ダブルクォート、シングルクォートの5文字を、&amp; &lt; &gt; &quot; &#39; のような形に変換します。これにより、文字列がタグや属性として解釈されず、あくまで「文字」として表示されるようになります。

XSS(クロスサイトスクリプティング)とは何ですか?

攻撃者が用意した文字列が、Webページ内でスクリプトやHTMLとして解釈・実行されてしまう脆弱性です。エスケープされないユーザー入力がそのまま画面に出力されると、攻撃者のscriptタグが動作し、Cookieやトークンの窃取、画面の改ざん、なりすまし操作などが可能になります。反射型・格納型・DOM型の3種類に大別されます。

どの文字を変換すればよいですか?

HTML本文では最低限 &(アンパサンド)、<(小なり)、>(大なり)の3つを、属性値の中ではさらに "(ダブルクォート)と '(シングルクォート)を変換します。&amp; を最初に変換するのが定石です。ただし安全に必要なエスケープは出力コンテキスト(HTML本文か、属性か、JavaScript内か、URL内か)によって変わるため、文脈に応じたエスケープを行うことが重要です。

← 技術ブログ一覧へ戻る