Java Tips | 文字列処理:HTMLアンエスケープ

Java Java
スポンサーリンク

HTMLアンエスケープは「エンコードされた文字を“元の文字”に戻す」技

HTMLエスケープは、<&&lt;&amp; に変換して「安全に表示する」ための技でした。
HTMLアンエスケープは、その逆で、&lt;&amp; のような「エンコードされた文字列」を、
元の <& といった“生の文字”に戻す処理です。

例えば、DBに &lt;b&gt;重要&lt;/b&gt; という文字列が保存されていて、
それを「HTMLとして解釈させたい」場面では、アンエスケープして <b>重要</b> に戻す必要があります。

ただし、Web画面にそのまま出すときはアンエスケープしてはいけない(XSSの危険がある)ので、
「どこでアンエスケープしてよいか」をきちんと意識することがとても重要です。


HTMLアンエスケープの基本:「エンティティを元の文字に戻す」

代表的なHTMLエンティティ

HTMLエスケープでよく使うエンティティは、次のようなものです。

&amp;&
&lt;<
&gt;>
&quot;"
&#39;'

アンエスケープは、これらを逆方向に変換する処理です。

&lt;b&gt;重要&lt;/b&gt;<b>重要</b>
Tom &amp; JerryTom & 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

ここで深掘りしたい重要ポイントは二つです。

一つ目は、「&amp; の置換を最後にしている」ことです。
もし最初に &amp;& に変えてしまうと、
&amp;lt; のような文字列が &lt; になり、さらにそれが < に変わる、といった“意図しない二段階変換”が起きる可能性があります。
そのため、「より長い・より具体的なエンティティから順に置換し、最後に &amp; を処理する」という順番が安全です。

二つ目は、「対象を“よく使うエンティティ”に絞っている」ことです。
HTMLには他にもたくさんのエンティティ(&nbsp; など)が存在しますが、
業務で最低限困らない範囲に絞ることで、実装をシンプルに保っています。
必要になったら、あとからエンティティを追加していくスタイルでも十分戦えます。


例題:ログやテキストとして“生のHTML”を見たいとき

「画面用にエスケープされた文字列」を人間が読む用に戻す

例えば、Web画面に出すためにエスケープされた文字列が、ログやDBに保存されているとします。

&lt;script&gt;alert('XSS');&lt;/script&gt;

これをログで見たとき、「実際にユーザーが何を入力したのか」を知りたいことがあります。
そのときに、アンエスケープしてからログに出すと、読みやすくなります。

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に保存するときに、
&lt;b&gt;重要&lt;/b&gt; のようにエスケープされた状態で持っているケースを考えます。

これを「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

ライブラリを使うメリットは、

  • 多数のエンティティ(&nbsp; など)を正しく扱ってくれる
  • サロゲートペアや特殊文字も含めて、仕様に沿った変換をしてくれる

といった点です。

一方で、自前ユーティリティを持つメリットは、

  • 「どこまでアンエスケープしてよいか」をプロジェクトごとに制御しやすい
  • 挙動がシンプルで、テストやレビューがしやすい

という点があります。

現場では、「基本はライブラリを使いつつ、“安全に使える範囲”を理解するために自前実装も知っておく」というスタンスが強いです。


まとめ:HTMLアンエスケープユーティリティで身につけたい感覚

HTMLアンエスケープは、「エスケープされた文字列を元の文字に戻す」だけのシンプルな処理に見えますが、
“どこで使うか”を間違えると一気に危険になる、扱いに注意が必要なテクニックです。

押さえておきたい感覚は、まず「&lt;&amp;<& に戻すのがアンエスケープ」であり、
その逆がエスケープだという対応関係。
次に、「置換の順番(特に &amp; を最後にする)を意識して、意図しない二段階変換を防ぐ」こと。
そして何より、「アンエスケープした結果を“ブラウザにそのまま返さない”」「ログやメールテンプレートなど、解釈されない/解釈させたい場所だけで使う」という線引きを、コードと設計で明確にすることです。

タイトルとURLをコピーしました