HTMLアンエスケープは「エンコードされた文字を“元の文字”に戻す」技
HTMLエスケープは、< や & を < や & に変換して「安全に表示する」ための技でした。
HTMLアンエスケープは、その逆で、< や & のような「エンコードされた文字列」を、
元の < や & といった“生の文字”に戻す処理です。
例えば、DBに <b>重要</b> という文字列が保存されていて、
それを「HTMLとして解釈させたい」場面では、アンエスケープして <b>重要</b> に戻す必要があります。
ただし、Web画面にそのまま出すときはアンエスケープしてはいけない(XSSの危険がある)ので、
「どこでアンエスケープしてよいか」をきちんと意識することがとても重要です。
HTMLアンエスケープの基本:「エンティティを元の文字に戻す」
代表的なHTMLエンティティ
HTMLエスケープでよく使うエンティティは、次のようなものです。
& → &< → <> → >" → "' → '
アンエスケープは、これらを逆方向に変換する処理です。
<b>重要</b> → <b>重要</b>Tom & Jerry → Tom & Jerry
つまり、「& から始まり ; で終わる“エンティティ”を、元の1文字に戻す」イメージです。
自前実装:シンプルなHTMLアンエスケープユーティリティ
まずは「よく使う5つ」だけを確実に戻す
最低限の用途なら、自前でシンプルなアンエスケープを書いても十分です。
public final class HtmlUnescaper {
private HtmlUnescaper() {}
public static String unescape(String text) {
if (text == null) {
return null;
}
String s = text;
s = s.replace("<", "<");
s = s.replace(">", ">");
s = s.replace(""", "\"");
s = s.replace("'", "'");
s = s.replace("&", "&");
return s;
}
}
Java使い方はこうなります。
System.out.println(HtmlUnescaper.unescape("<b>重要</b>"));
// <b>重要</b>
System.out.println(HtmlUnescaper.unescape("Tom & Jerry"));
// Tom & Jerry
System.out.println(HtmlUnescaper.unescape(""quote" and 'single'"));
// "quote" and 'single'
Javaここで深掘りしたい重要ポイントは二つです。
一つ目は、「& の置換を最後にしている」ことです。
もし最初に & を & に変えてしまうと、&lt; のような文字列が < になり、さらにそれが < に変わる、といった“意図しない二段階変換”が起きる可能性があります。
そのため、「より長い・より具体的なエンティティから順に置換し、最後に & を処理する」という順番が安全です。
二つ目は、「対象を“よく使うエンティティ”に絞っている」ことです。
HTMLには他にもたくさんのエンティティ( など)が存在しますが、
業務で最低限困らない範囲に絞ることで、実装をシンプルに保っています。
必要になったら、あとからエンティティを追加していくスタイルでも十分戦えます。
例題:ログやテキストとして“生のHTML”を見たいとき
「画面用にエスケープされた文字列」を人間が読む用に戻す
例えば、Web画面に出すためにエスケープされた文字列が、ログやDBに保存されているとします。
<script>alert('XSS');</script>
これをログで見たとき、「実際にユーザーが何を入力したのか」を知りたいことがあります。
そのときに、アンエスケープしてからログに出すと、読みやすくなります。
public final class LogHelper {
private LogHelper() {}
public static void logRawInput(String escaped) {
String raw = HtmlUnescaper.unescape(escaped);
System.out.println("RAW INPUT: " + raw);
}
}
Java使い方はこうです。
LogHelper.logRawInput("<script>alert('XSS');</script>");
// RAW INPUT: <script>alert('XSS');</script>
Javaここでのポイントは、「ログやテキストとして“人間が読むだけ”なら、アンエスケープしても安全」ということです。
ブラウザで解釈される場所(HTMLとして表示される場所)に出さない限り、<script> はただの文字列であり、実行されることはありません。
例題:テンプレートやメール本文で「HTMLとして解釈させたい」場合
「DBにはエスケープ済み、出すときにアンエスケープしてHTMLとして使う」
例えば、メールテンプレートをDBに保存するときに、<b>重要</b> のようにエスケープされた状態で持っているケースを考えます。
これを「HTMLメールとして送る」ときには、アンエスケープして <b>重要</b> に戻し、
HTMLとして解釈させたいことがあります。
public final class MailTemplate {
private MailTemplate() {}
public static String buildHtmlBody(String escapedTemplate) {
String html = HtmlUnescaper.unescape(escapedTemplate);
// ここでさらにプレースホルダ置換などを行うイメージ
return html;
}
}
Java使い方はこうです。
String template = "これは <b>重要</b> なお知らせです。";
String body = MailTemplate.buildHtmlBody(template);
// body: これは <b>重要</b> なお知らせです。
Javaここでの重要ポイントは、「アンエスケープした結果を“どこに出すか”を厳密に管理する」ことです。
HTMLメールの本文として送るのはOKですが、
同じ文字列をWeb画面にそのまま出すのはNGです(XSSの危険がある)。
「このアンエスケープは“HTMLとして解釈させるためのもの”であり、
ブラウザにそのまま返す用途には使わない」というルールを、
クラス名やメソッド名、コメントで明確にしておくと安全です。
ライブラリを使う場合との違いと使い分け
Apache Commons Text などの既存実装
HTMLエスケープと同様に、アンエスケープもライブラリで提供されています。
例えば、Apache Commons Text には StringEscapeUtils.unescapeHtml4 があります。
import org.apache.commons.text.StringEscapeUtils;
String raw = StringEscapeUtils.unescapeHtml4("<b>重要</b> & "test"");
// <b>重要</b> & "test"
Javaライブラリを使うメリットは、
- 多数のエンティティ(
など)を正しく扱ってくれる - サロゲートペアや特殊文字も含めて、仕様に沿った変換をしてくれる
といった点です。
一方で、自前ユーティリティを持つメリットは、
- 「どこまでアンエスケープしてよいか」をプロジェクトごとに制御しやすい
- 挙動がシンプルで、テストやレビューがしやすい
という点があります。
現場では、「基本はライブラリを使いつつ、“安全に使える範囲”を理解するために自前実装も知っておく」というスタンスが強いです。
まとめ:HTMLアンエスケープユーティリティで身につけたい感覚
HTMLアンエスケープは、「エスケープされた文字列を元の文字に戻す」だけのシンプルな処理に見えますが、
“どこで使うか”を間違えると一気に危険になる、扱いに注意が必要なテクニックです。
押さえておきたい感覚は、まず「< や & を < や & に戻すのがアンエスケープ」であり、
その逆がエスケープだという対応関係。
次に、「置換の順番(特に & を最後にする)を意識して、意図しない二段階変換を防ぐ」こと。
そして何より、「アンエスケープした結果を“ブラウザにそのまま返さない”」「ログやメールテンプレートなど、解釈されない/解釈させたい場所だけで使う」という線引きを、コードと設計で明確にすることです。
