はじめに 「HTMLエスケープ」は“文字列をHTMLとして誤解させないための防御”
Web画面を作るとき、ユーザーが入力した文字列をそのままHTMLに埋め込むと、とても危険です。
なぜなら、<script> や <b> のような「HTMLとして意味を持つ記号」が、そのまま“タグ”として解釈されてしまうからです。
HTMLエスケープは、一言でいうと、
「ただの文字列」を「HTMLとして解釈されない安全な文字列」に変換する処理
です。
例えば、次のような文字列を考えます。
こんにちは <b>太郎</b> さん
これをそのままHTMLに出すと、「太郎」が太字になります。
でも、「ユーザーが入力したものは“ただのテキスト”として表示したい」なら、
こんにちは <b>太郎</b> さん
のように変換しておく必要があります。
これが「HTMLエスケープ」です。
なぜHTMLエスケープが必要なのか(XSSの入り口をふさぐ)
「タグとして動いてほしくないもの」をただの文字にする
HTMLエスケープの一番大事な目的は、XSS(クロスサイトスクリプティング)を防ぐことです。
例えば、ユーザーがこんな文字列を入力したとします。
<script>alert('XSS');</script>
これをエスケープせずに画面に埋め込むと、
ブラウザは「ただの文字」ではなく「JavaScriptコード」として実行してしまいます。
でも、HTMLエスケープしておけば、
<script>alert('XSS');</script>
となり、ブラウザは「タグ」ではなく「テキスト」として表示します。
つまり、「危険なものを“ただの文字”に変える防御壁」がHTMLエスケープです。
C#での基本:HtmlEncode(HTMLエスケープ)を使う
代表的な2つのAPI
C#(.NET)でHTMLエスケープをするには、主に次の2つを使います。
System.Web.HttpUtility.HtmlEncodeSystem.Net.WebUtility.HtmlEncode
Webアプリ(ASP.NET系)なら HttpUtility がよく出てきますが、
最近は WebUtility のほうが依存が少なくて扱いやすいです。
ここでは WebUtility.HtmlEncode を使った例で説明します。
using System;
using System.Net;
public static class HtmlEscapeUtil
{
public static string Escape(string? value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return WebUtility.HtmlEncode(value);
}
}
C#動作例
Console.WriteLine(HtmlEscapeUtil.Escape("こんにちは <b>太郎</b> さん"));
// こんにちは <b>太郎</b> さん
Console.WriteLine(HtmlEscapeUtil.Escape("<script>alert('XSS');</script>"));
// <script>alert('XSS');</script>
C#ここで注目してほしいのは、< や > だけでなく、シングルクォート ' も ' のように変換されていることです。
これにより、HTML属性の中に埋め込んだときの安全性も高まります。
どんな文字がエスケープされるのか
代表的な変換例
HTMLエスケープでは、主に次のような文字が変換されます。
<→<>→>&→&"→"'→'(または')
これらは、HTMLの中で特別な意味を持つ記号です。
そのまま出すと「タグの開始」「属性の区切り」「エンティティの開始」などとして解釈されてしまいます。
エスケープすることで、「これは記号じゃなくて“文字”ですよ」とブラウザに伝えることができます。
HTMLエスケープと逆変換(デコード)
HtmlDecode で元に戻せる
エスケープした文字列を、元の文字列に戻したい場面もあります。
そのときは、HtmlDecode を使います。
using System.Net;
string encoded = "こんにちは <b>太郎</b> さん";
string decoded = WebUtility.HtmlDecode(encoded);
Console.WriteLine(decoded);
// こんにちは <b>太郎</b> さん
C#HtmlEncode と HtmlDecode はセットで覚えておくと便利です。
実務での設計ポイントと注意点
どこでエスケープするかを「層」で決める
よくある失敗は、「どこでエスケープするか」がバラバラになることです。
- DBに保存する前にエスケープする
- 画面に出す直前にエスケープする
- どこかのサービス層でエスケープする
これが混ざると、「二重エスケープ」「エスケープ漏れ」が起きやすくなります。
おすすめは、
「DBには“生の値”を保存し、画面に出す直前でエスケープする」
というルールに統一することです。
- DB:ユーザーが入力したそのまま(ただしバリデーションは別途)
- HTML出力:ビューやテンプレート側で必ずエスケープ
こうしておくと、「どの層で何をしているか」が明確になり、バグが減ります。
二重エスケープに注意する
例えば、すでにエスケープされた文字列に、さらに HtmlEncode をかけるとこうなります。
string once = WebUtility.HtmlEncode("<b>太郎</b>");
// once: <b>太郎</b>
string twice = WebUtility.HtmlEncode(once);
// twice: <b>太郎</b>
C#画面に twice を出すと、<b>太郎</b> がそのまま表示されてしまい、
「タグとしてもテキストとしても中途半端」な状態になります。
なので、
- 「この値はすでにエスケープ済みか?」
- 「この層でエスケープするのは正しいか?」
を意識して設計することが大事です。
HTMLエスケープユーティリティとしてまとめる
「とりあえずこれを通せば安全」という窓口を1つ作る
プロジェクト全体で使う「HTMLエスケープ窓口」を1つ用意しておくと、
あとからの変更や調整がとても楽になります。
using System.Net;
public static class HtmlSafe
{
public static string Encode(string? value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return WebUtility.HtmlEncode(value);
}
public static string Decode(string? value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return WebUtility.HtmlDecode(value);
}
}
C#ビューやテンプレート側では、常にこの HtmlSafe.Encode を通すようにしておけば、
- 将来、別のエスケープ方法に変えたくなった
- ログ出力時だけは別のルールにしたい
といったときも、このユーティリティを直すだけで済みます。
まとめ 「HTMLエスケープユーティリティ」は“文字列を安全に画面へ渡すための最後のフィルタ”
HTMLエスケープは、Webアプリにおける超基本かつ超重要な防御テクニックです。
押さえておきたいポイントは、
- HTMLエスケープは「タグやスクリプトとして解釈されないようにする」ための変換
- C#では
WebUtility.HtmlEncode/HtmlDecodeを使うのが基本 <,>,&,",'などの記号がエスケープされる- DBには生の値を保存し、「画面に出す直前でエスケープ」に統一すると設計がきれいになる
- 二重エスケープを避けるために、「どこでエスケープするか」をユーティリティ+ルールで固定する
ここまで理解できれば、「なんとなくエスケープしている」段階から一歩進んで、
“XSSを意識した、安全な文字列処理の設計”を、自分のC#ユーティリティとして形にしていけるようになります。
