JavaでHTMLをエスケープするための推奨される方法


262

脱出する推奨方法はあります<>"および&プレーンなJavaコードでHTMLを出力する際に、文字?(手動で以下を実行することを除いて、つまり)。

String source = "The less than sign (<) and ampersand (&) must be escaped before using them in HTML";
String escaped = source.replace("<", "&lt;").replace("&", "&amp;"); // ...

2
引用符で囲まれていないHTML属性に出力する場合、スペース、タブ、バックスペースなどの他の文字により、攻撃者はリストされている文字なしでJavaScript属性を導入できる可能性があることに注意してください。詳細については、OWASP XSS防止に関するチートシートを参照してください。
ジェフウィリアムズ

ところで、このコードでは、「&」の前に「&」をエスケープして、これが正しく機能するようにする必要があります(「&lt;」は「&amp; lt;」に置き換えられます。それ以外の場合は、「<」ではなく「&lt;」としてレンダリングされます"):source.replace("&", "&amp;").replace("<", "&lt;");
Tey '

回答:


261

StringEscapeUtilsからのApache Commonsのラング

import static org.apache.commons.lang.StringEscapeUtils.escapeHtml;
// ...
String source = "The less than sign (<) and ampersand (&) must be escaped before using them in HTML";
String escaped = escapeHtml(source);

以下の場合、バージョン3

import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
// ...
String escaped = escapeHtml4(source);

2
一方でStringEscapeUtilsいいです、あなたがHTML / XMLの空白の正規化を避けたい場合には、属性の空白適切にエスケープしません。詳細については、私の回答を参照してください。
Adam Gent

21
上記の例は壊れています。escapeHtml4()メソッドを使用してください。
stackoverflowuser2010

3
グアバのファンについては、以下のokranzの回答を参照してください。
George Hawkins

2
WebページにUTF-8エンコードがある場合、必要なのは次の5つのASCII文字のみをエスケープするGuavaのhtmlEscaperです: '"&<>。ApacheのescapeHtml()は、UTF-8 Webでは不要と思われるアクセントを含む非ASCII文字も置き換えますページ?
zdenekca

4
commons-lang3では非推奨になりました。commons.apache.org/proper/commons-text
Danny

137

Apache Commonsの代替:SpringHtmlUtils.htmlEscape(String input)方法を使用します。


9
ありがとう。私は、(代わりにそれを使用してきましたStringEscapeUtils.escapeHtml()から、apache-commonsそのままではロシア語の文字を残しているため2.6)。
Slava Semushin

6
知っておくと便利です。TBH私は最近、Apacheのものに広いバースを与えます。
アダムスキー2012

1
私も使いましたが、漢字もそのまま残しています。
smartwjw 2015年

下記のグアバ代替とどのように比較しますか?
vishvAs vAsuki 2017

2
また、アポストロフィもエンコードするため、Apache StringEscapeUtilsとは異なり、実際に役立ちます
DavidBalažicSep

57

素敵な短い方法:

public static String escapeHTML(String s) {
    StringBuilder out = new StringBuilder(Math.max(16, s.length()));
    for (int i = 0; i < s.length(); i++) {
        char c = s.charAt(i);
        if (c > 127 || c == '"' || c == '\'' || c == '<' || c == '>' || c == '&') {
            out.append("&#");
            out.append((int) c);
            out.append(';');
        } else {
            out.append(c);
        }
    }
    return out.toString();
}

https://stackoverflow.com/a/8838023/1199155に基づいています(アンプがありません)。http://www.w3.org/TR/html4/sgml/entities.htmlによると、if句でチェックされる4文字は128未満の唯一の文字です


いいね。エンコーディングの「htmlバージョン」は使用しません(例:「á」は「&#225;」ではなく「&aacute;」になります)。ただし、数値のものはIE7でも機能するため、私は使用しないと思います心配する必要があります。ありがとう。
nonzaprej 2017

OPが4つの関連する文字をエスケープするように要求したときに、なぜそれらすべての文字をエンコードするのですか?CPUとメモリを浪費しています。
David Bala Bic

1
アポストロフィを忘れました。したがって、引用符で囲まれていない属性を、このコードを使用して属性値をエスケープするすべての場所に挿入できます。
David Bala Bic

45

Apache Commons Langライブラリの新しいバージョンがあり、別のパッケージ名(org.apache.commons.lang3)を使用しています。にStringEscapeUtilsは、さまざまなタイプのドキュメントをエスケープするためのさまざまな静的メソッドがあります(http://commons.apache.org/proper/commons-lang/javadocs/api-3.0/index.html)。したがって、HTMLバージョン4.0の文字列をエスケープするには:

import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;

String output = escapeHtml4("The less than sign (<) and ampersand (&) must be escaped before using them in HTML");

3
残念ながら、何もHTML 5のために存在していない、またApacheのドキュメントは、HTML 5のためescapeHtml4を使用することが適切であるかどうかを指定します
ポール・ヴィンセント・クレイヴン

43

Google Guavaを使用する場合:

import com.google.common.html.HtmlEscapers;
[...]
String source = "The less than sign (<) and ampersand (&) must be escaped before using them in HTML";
String escaped = HtmlEscapers.htmlEscaper().escape(source);

40

Android(API 16以降)では、次のことができます。

Html.escapeHtml(textToScape);

またはより低いAPIの場合:

TextUtils.htmlEncode(textToScape);

escapeHtml代わりに使用する理由はありますhtmlEncodeか?
Muz

2
これら2つの違いについての私の私の質問も参照してください。(@Muz)
JonasCz-2016年

37

これに注意してください。HTMLドキュメント内には、さまざまな「コンテキスト」がいくつかあります:要素内、引用符付きの属性値、引用符なしの属性値、URL属性、JavaScript、CSSなど...それぞれに異なるエンコード方法を使用する必要があります。これらは、クロスサイトスクリプティング(XSS)を防止します。これらの各コンテキストの詳細については、OWASP XSS防止チートシートを確認してください。これらの各コンテキストのエスケープメソッドは、OWASP ESAPIライブラリ(https://github.com/ESAPI/esapi-java-legacy)にあります


6
出力をエンコードするコンテキストが非常に重要であることを指摘していただきありがとうございます。「エンコード」という用語も、「エスケープ」よりもはるかに適切な動詞です。エスケープは、「この文字列をXHTML属性/ SQLクエリパラメータ/ PostScript印刷文字列/ CSV出力フィールドにどのようにエンコードするのか」とは対照的に、何らかの特別なハックを意味します
Roboprog

5
「エンコード」と「エスケープ」はどちらも、これを説明するために広く使用されています。「エスケープ」という用語は通常、プロセスが「エスケープ文字」を構文的に関連する文字の前に追加する場合に使用されます。たとえば、バックスラッシュ\で引用文字をエスケープするなどです。文字を別の形式に変換します(引用文字%22をエンコードするURL、&#x22または@quotとしてHTMLエンティティをエンコードするなど)
Jeff Williams


1
グーグルを節約するには、Encoderクラスstatic.javadoc.io/org.owasp.esapi/esapi/2.0.1/org/owasp/esapi/…を
Jakub Bochenski

14

いくつかの目的のために、HtmlUtils

import org.springframework.web.util.HtmlUtils;
[...]
HtmlUtils.htmlEscapeDecimal("&"); //gives &#38;
HtmlUtils.htmlEscape("&"); //gives &amp;

1
春のHtmlUtilsコメントから:* <p>包括的な文字列エスケープユーティリティセットについては、* Apache Commons LangとそのStringEscapeUtilsクラスを検討してください。*ここでは、HTMLエスケープのためだけにCommons Langでのランタイム依存を回避するためにそのクラスを使用していません。さらに、Springの* HTMLエスケープはより柔軟で、100%HTML 4.0に準拠しています。プロジェクトですでにApacheコモンズを使用している場合は、おそらくApacheのStringEscapeUtilsを使用する必要があります
andreyro

10

@dfaの答えorg.apache.commons.lang.StringEscapeUtils.escapeHtmlは素晴らしいですが、過去に使用しましたが、HTML(またはXML)属性のエスケープには使用しないでください。そうしないと、空白が正規化されます(つまり、隣接するすべての空白文字が1つのスペースになります)。

私がこれを知っているのは、空白が保持されなかった属性について、ライブラリ(JATL)に対してバグを報告したためです。したがって、私は属性(要素のコンテンツのエスケープ)を区別する(コピーと貼り付け)クラス(JDOMからいくつかを盗みました)のドロップを持っています

これは以前はそれほど問題にならなかったかもしれませんが(適切な属性エスケープ)、HTML5のdata-属性使用法の使用を考えると、ますます関心が高まっています。


9

org.apache.commons.lang3.StringEscapeUtilsは非推奨になりました。ここでorg.apache.commons.text.StringEscapeUtilsを使用する必要があります

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>${commons.text.version}</version>
    </dependency>

1

ほとんどのライブラリは、何百ものシンボルや、ASCII以外の何千もの文字を含む、できる限りすべてをエスケープしますが、これはUTF-8の世界では望んでいないことです。

また、Jeff Williamsが述べたように、「エスケープHTML」オプションは1つではなく、いくつかのコンテキストがあります。

引用符で囲まれていない属性を決して使用しないと仮定し、異なるコンテキストが存在することを念頭に置いて、独自のバージョンを作成しました。

private static final long BODY_ESCAPE =
        1L << '&' | 1L << '<' | 1L << '>';
private static final long DOUBLE_QUOTED_ATTR_ESCAPE =
        1L << '"' | 1L << '&' | 1L << '<' | 1L << '>';
private static final long SINGLE_QUOTED_ATTR_ESCAPE =
        1L << '"' | 1L << '&' | 1L << '\'' | 1L << '<' | 1L << '>';

// 'quot' and 'apos' are 1 char longer than '#34' and '#39' which I've decided to use
private static final String REPLACEMENTS = "&#34;&amp;&#39;&lt;&gt;";
private static final int REPL_SLICES = /*  |0,   5,   10,  15, 19, 23*/
        5<<5 | 10<<10 | 15<<15 | 19<<20 | 23<<25;
// These 5-bit numbers packed into a single int
// are indices within REPLACEMENTS which is a 'flat' String[]

private static void appendEscaped(
        StringBuilder builder,
        CharSequence content,
        long escapes // pass BODY_ESCAPE or *_QUOTED_ATTR_ESCAPE here
) {
    int startIdx = 0, len = content.length();
    for (int i = 0; i < len; i++) {
        char c = content.charAt(i);
        long one;
        if (((c & 63) == c) && ((one = 1L << c) & escapes) != 0) {
        // -^^^^^^^^^^^^^^^   -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        // |                  | take only dangerous characters
        // | java shifts longs by 6 least significant bits,
        // | e. g. << 0b110111111 is same as >> 0b111111.
        // | Filter out bigger characters

            int index = Long.bitCount(SINGLE_QUOTED_ATTR_ESCAPE & (one - 1));
            builder.append(content, startIdx, i /* exclusive */)
                    .append(REPLACEMENTS,
                            REPL_SLICES >>> 5*index & 31,
                            REPL_SLICES >>> 5*(index+1) & 31);
            startIdx = i + 1;
        }
    }
    builder.append(content, startIdx, len);
}

行の長さの制限なしにGistからコピーして貼り付けることを検討してください。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.