はじめに 「HTMLアンエスケープ」は“記号に戻して、元のテキストを取り出す”技
さっきの「HTMLエスケープ」は、< や > を < や > に変えて「安全な文字列」にする処理でした。
HTMLアンエスケープは、その逆です。
<b>太郎</b>
→ "<b>太郎</b>" に戻す処理。
「画面に出すためにエスケープしたもの」や
「外部システムからエスケープ済みで渡されたもの」を、
“人間が読む元の形”に戻したいときに使います。
ここでは、初心者向けに、
- HTMLアンエスケープの役割
- C#での基本API(HtmlDecode)
- 具体的な変換例
- エスケープとの組み合わせ方・注意点
- 業務ユーティリティとしてのまとめ方
を、例題付きでかみ砕いて説明していきます。
HTMLアンエスケープの役割をイメージする
「エンティティ」を“ふつうの文字”に戻す
HTMLの世界では、特別な意味を持つ記号を「エンティティ」という形で表現します。
例えば:
<→<>→>&→&"→"'→'
エスケープは「記号 → エンティティ」への変換。
アンエスケープは「エンティティ → 記号」への変換です。
たとえば、ログやDBにこんな文字列が入っていたとします。
こんにちは <b>太郎</b> さん
これをそのまま画面に出すと、「<b>」がそのまま見えてしまいます。
でも、人間が読みたいのは本来の形:
こんにちは <b>太郎</b> さん
この「元の形に戻す」のが、HTMLアンエスケープです。
C#での基本:HtmlDecode を使う
WebUtility.HtmlDecode の使い方
C# では、HTMLアンエスケープには主に次のAPIを使います。
System.Net.WebUtility.HtmlDecode- (ASP.NET系なら)
System.Web.HttpUtility.HtmlDecode
ここでは依存の少ない WebUtility を使います。
using System;
using System.Net;
public static class HtmlUnescapeUtil
{
public static string Unescape(string? value)
{
if (string.IsNullOrEmpty(value))
{
return string.Empty;
}
return WebUtility.HtmlDecode(value);
}
}
C#動作例
Console.WriteLine(HtmlUnescapeUtil.Unescape("こんにちは <b>太郎</b> さん"));
// こんにちは <b>太郎</b> さん
Console.WriteLine(HtmlUnescapeUtil.Unescape("<script>alert('XSS');</script>"));
// <script>alert('XSS');</script>
C#HtmlDecode は、< や ' のようなエンティティを自動的に解釈して、
元の文字に戻してくれます。
どんなものがアンエスケープされるのか
代表的なエンティティの変換
HtmlDecode は、HTMLで定義されているエンティティを解釈してくれます。
代表的なものは次の通りです。
<→<>→>&→&"→"'→'あ→あ(数値文字参照)
つまり、「& から始まって ; で終わる“特別な書き方”」を見つけて、
それを本来の文字に戻してくれるイメージです。
数値文字参照もちゃんと戻る
数値文字参照(&#数字; や 進数;)もアンエスケープされます。
Console.WriteLine(HtmlUnescapeUtil.Unescape("A")); // A
Console.WriteLine(HtmlUnescapeUtil.Unescape("あ")); // あ
C#外部システムから「全部エンティティ化されたテキスト」が来ることもあるので、
こうしたケースでも HtmlDecode を通せば、普通の文字列として扱えるようになります。
HTMLエスケープとのセット運用を意識する
Encode → Decode の往復
HTMLエスケープ(Encode)とアンエスケープ(Decode)は、基本的にセットです。
using System.Net;
string original = "<b>太郎 & 次郎</b>";
string encoded = WebUtility.HtmlEncode(original);
string decoded = WebUtility.HtmlDecode(encoded);
Console.WriteLine(original); // <b>太郎 & 次郎</b>
Console.WriteLine(encoded); // <b>太郎 & 次郎</b>
Console.WriteLine(decoded); // <b>太郎 & 次郎</b>
C#original → encoded → decoded とたどると、
元の文字列に戻っているのが分かります。
「どこでEncode」「どこでDecode」するかを決める
設計として大事なのは、
- DBには「生の文字列」を保存するのか、「エスケープ済み」を保存するのか
- 画面に出す直前で必ず
HtmlEncodeするのか - 外部から来たデータが「すでにエスケープ済み」なのか
を、層ごとにきちんと決めておくことです。
典型的なパターンは:
- DB:生の文字列(エスケープしない)
- HTML出力:ビュー側で
HtmlEncode(=エスケープ) - 外部から「エスケープ済み文字列」が来たときだけ、必要に応じて
HtmlDecode
アンエスケープは、「すべての文字列に対して毎回やるもの」ではなく、
“エスケープ済みのものを元に戻したいときだけ使う” という意識が大事です。
注意点:アンエスケープしたものをそのままHTMLに出さない
XSSの逆流に気をつける
例えば、外部からこんな文字列が来たとします。
<script>alert('XSS');</script>
これを HtmlDecode すると、こうなります。
<script>alert('XSS');</script>
もし、これを そのままHTMLに埋め込んで表示してしまうと、
ブラウザは <script> として解釈し、JavaScriptを実行してしまいます。
つまり、
- 「画面に出す前にアンエスケープ」してしまうと危険
- 「画面に出すときは、Encode(エスケープ)された状態であるべき」
ということです。
アンエスケープは、
- ログを読むために元の文字を見たい
- テキスト処理(検索・置換など)をするために、エンティティを普通の文字に戻したい
といった「内部処理」向けに使うことが多いです。
業務ユーティリティとしてまとめる
Encode と Decode を同じ窓口にまとめる
プロジェクト全体で使う「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(model.Text) - 外部から「エスケープ済み文字列」が来たときに内部処理したい:
HtmlSafe.Decode(input)
こうしておくと、「どこかで勝手に HtmlDecode してしまってXSSの入り口を作る」といった事故を減らせます。
まとめ 「HTMLアンエスケープユーティリティ」は“エンティティだらけの文字列を、人間が読める形に戻す道具”
HTMLアンエスケープは、HTMLエスケープの“逆操作”ですが、
使いどころを間違えるとセキュリティリスクにもつながります。
押さえておきたいポイントは、
- アンエスケープは「
<や'を元の文字に戻す」処理 - C#では
WebUtility.HtmlDecodeを使うのが基本 - Encode ↔ Decode は往復可能だが、「どの層で何をするか」を決めておくことが重要
- 画面に出す前に Decode してはいけない(XSSの原因になる)
- Encode / Decode を1つのユーティリティにまとめて、使いどころを明確にする
ここまで理解できれば、「なんとなく HtmlDecode を呼んでいる」段階から一歩進んで、
“安全性と可読性のバランスを取った文字列処理”として、
HTMLアンエスケープを自分のC#ユーティリティにきちんと組み込めるようになっていきます。
