Empfohlene Methode zum Escapezeichen von HTML in Java

262

Gibt es einen empfohlene Weg zu entkommen <, >, "und &Zeichen bei der Ausgabe HTML in einfachen Java - Code? (Anders als manuell das Folgende zu tun).

String source = "The less than sign (<) and ampersand (&) must be escaped before using them in HTML";
String escaped = source.replace("<", "&lt;").replace("&", "&amp;"); // ...
Ben Lings
quelle
2
Beachten Sie, dass andere Zeichen wie Leerzeichen, Tabulatoren, Rücktasten usw. es Angreifern ermöglichen können, Javascript-Attribute ohne eines der aufgelisteten Zeichen einzuführen, wenn Sie in ein nicht zitiertes HTML-Attribut ausgeben. Weitere Informationen finden Sie im OWASP XSS Prevention Cheat Sheet.
Jeff Williams
Übrigens sollten Sie in diesem Code "&" vor "<" maskieren, damit dies ordnungsgemäß funktioniert ("& lt;" wird durch "& amp; lt;" ersetzt, andernfalls wird dies als "& lt;" gerendert, dann nicht "<" "):source.replace("&", "&amp;").replace("<", "&lt;");
Tey '23.

Antworten:

261

StringEscapeUtils von Apache Commons Lang :

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);

Für Version 3 :

import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
// ...
String escaped = escapeHtml4(source);
dfa
quelle
2
Obwohl StringEscapeUtilses nett ist, wird das Leerzeichen für Attribute nicht ordnungsgemäß ausgeblendet, wenn Sie die Normalisierung von HTML / XML-Leerzeichen vermeiden möchten. Siehe meine Antwort für mehr Details.
Adam Gent
21
Das obige Beispiel ist kaputt. Verwenden Sie jetzt die EscapeHtml4 () -Methode.
stackoverflowuser2010
3
Für Guavenfans siehe die Antwort von okranz unten.
George Hawkins
2
Wenn die Webseite über eine UTF-8-Codierung verfügt, benötigen wir lediglich den htmlEscaper von Guava, der nur die folgenden fünf ASCII-Zeichen enthält: '"& <>. Das Escape-HTML () des Apache ersetzt auch Nicht-ASCII-Zeichen, einschließlich Akzente, die im UTF-8-Web unnötig erscheinen Seiten?
Zdenekca
4
Es ist jetzt in commons-lang3 veraltet. Es wurde nach commons.apache.org/proper/commons-text
Danny
137

Eine Alternative zu Apache Commons: Verwenden Sie Spring 'sHtmlUtils.htmlEscape(String input) Methode.

Adamski
quelle
9
Vielen Dank. Ich habe es verwendet (anstelle StringEscapeUtils.escapeHtml()von apache-commons2.6), weil es die russischen Zeichen unverändert lässt.
Slava Semushin
6
Das ist gut zu wissen. TBH Ich mache Apache-Sachen heutzutage einen großen Bogen.
Adamski
1
Ich habe es auch benutzt, es lässt auch chinesische Schriftzeichen wie sie sind.
Smartwjw
Wie ist es mit der unten erwähnten Guavenalternative zu vergleichen?
vishvAs vAsuki
2
Und es codiert auch den Apostroph, so dass es im Gegensatz zu Apache StringEscapeUtils
David Balažic
57

Schöne kurze Methode:

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();
}

Basierend auf https://stackoverflow.com/a/8838023/1199155 (der Verstärker fehlt dort). Die vier in der if-Klausel aktivierten Zeichen sind laut http://www.w3.org/TR/html4/sgml/entities.html die einzigen unter 128

Bruno Eberhard
quelle
Nett. Es werden nicht die "HTML-Versionen" der Codierungen verwendet (Beispiel: "á" wäre "& aacute;" anstelle von "& # 225;"), aber da die numerischen auch in IE7 funktionieren, glaube ich nicht sich Sorgen müssen. Vielen Dank.
Nonzaprej
Warum codieren Sie all diese Zeichen, wenn das OP Sie auffordert, den 4 relevanten Zeichen zu entkommen? Sie verschwenden CPU und Speicher.
David Balažic
1
Du hast den Apostroph vergessen. So können Benutzer überall dort, wo dieser Code verwendet wird, um Attributwerte zu umgehen, nicht zitierte Attribute einfügen.
David Balažic
45

Es gibt eine neuere Version der Apache Commons Lang-Bibliothek und sie verwendet einen anderen Paketnamen (org.apache.commons.lang3). Das hat StringEscapeUtilsjetzt verschiedene statische Methoden, um verschiedene Arten von Dokumenten zu maskieren ( maskieren http://commons.apache.org/proper/commons-lang/javadocs/api-3.0/index.html ). So entziehen Sie sich der HTML-Version 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");
Martin Dimitrov
quelle
3
Leider gibt es für HTML 5 nichts und in den Apache-Dokumenten ist auch nicht angegeben, ob die Verwendung von EscapeHtml4 für HTML 5 angemessen ist.
Paul Vincent Craven
43

Für diejenigen, die Google Guava verwenden:

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);
okrasz
quelle
40

Auf Android (API 16 oder höher) können Sie:

Html.escapeHtml(textToScape);

oder für niedrigere API:

TextUtils.htmlEncode(textToScape);
OriolJ
quelle
Gibt es einen Grund, escapeHtmlstatt zu verwenden htmlEncode?
Muz
2
Siehe auch meine Frage zum Unterschied zwischen diesen beiden. (@ Muz)
JonasCz
37

Sei vorsichtig damit. Innerhalb eines HTML-Dokuments gibt es eine Reihe verschiedener 'Kontexte': Innerhalb eines Elements, zitierter Attributwert, nicht zitierter Attributwert, URL-Attribut, Javascript, CSS usw. Sie müssen für jedes Element eine andere Codierungsmethode verwenden diese, um Cross-Site Scripting (XSS) zu verhindern. Überprüfen Sie das OWASP XSS Prevention Cheat Sheet auf Details zu jedem dieser Kontexte. Escape-Methoden für jeden dieser Kontexte finden Sie in der OWASP ESAPI-Bibliothek - https://github.com/ESAPI/esapi-java-legacy .

Jeff Williams
quelle
6
DANKE, dass Sie darauf hingewiesen haben, dass der Kontext, in dem Sie die Ausgabe codieren möchten, sehr wichtig ist. Der Begriff "codieren" ist auch ein viel passenderes Verb als "entkommen". Flucht impliziert eine gewisse Art von speziellem Hack, im Gegensatz zu „Wie kann ich kodieren diese Zeichenfolge für: ein XHTML - Attribut / SQL - Abfrage - Parameter / Postscript - Druck Zeichenfolge / CSV Ausgabefeld
Roboprog
5
'Encode' und 'Escape' werden häufig verwendet, um dies zu beschreiben. Der Begriff "Escape" wird im Allgemeinen verwendet, wenn vor einem syntaktisch relevanten Zeichen ein "Escape-Zeichen" eingefügt werden soll, z. B. das Escape-Zeichen eines Anführungszeichens mit einem Backslash. "Der Begriff" encode "wird normalerweise verwendet, wenn Sie a übersetzen Zeichen in eine andere Form, z. B. URL-Codierung des Anführungszeichens% 22 oder HTML-Entitätscodierung als & # x22 oder @quot.
Jeff Williams
1
Suchen
Jakub Bochenski,
14

Für einige Zwecke, HtmlUtils :

import org.springframework.web.util.HtmlUtils;
[...]
HtmlUtils.htmlEscapeDecimal("&"); //gives &#38;
HtmlUtils.htmlEscape("&"); //gives &amp;
AUU
quelle
1
Aus den Kommentaren von HtmlUtils im Frühjahr: * <p> Betrachten Sie Apache Commons Lang und seine StringEscapeUtils-Klasse, um einen umfassenden Satz von Dienstprogrammen zum Entkommen von Zeichenfolgen zu erhalten. * Wir verwenden diese Klasse hier nicht, um eine Laufzeitabhängigkeit * von Commons Lang zu vermeiden, nur um HTML zu maskieren. Darüber hinaus ist Spring * HTML-Escape flexibler und 100% HTML 4.0-kompatibel. Wenn Sie bereits Apache Commons in Ihrem Projekt verwenden, sollten Sie wahrscheinlich die StringEscapeUtils von Apache
andreyro
10

Die @ dfa-Antwort von org.apache.commons.lang.StringEscapeUtils.escapeHtmlist zwar nett und ich habe sie in der Vergangenheit verwendet. Sie sollte jedoch nicht zum Escapen von HTML- (oder XML-) Attributen verwendet werden, da sonst das Leerzeichen normalisiert wird (dh alle benachbarten Leerzeichen werden zu einem einzigen Leerzeichen).

Ich weiß das, weil ich Fehler in meiner Bibliothek (JATL) für Attribute eingereicht habe, bei denen Leerzeichen nicht beibehalten wurden. Daher habe ich eine Drop-In- Klasse (Kopieren und Einfügen) (von der ich einige aus JDOM gestohlen habe), die das Entkommen von Attributen und Elementinhalten unterscheidet .

Während dies in der Vergangenheit möglicherweise nicht so wichtig war (ordnungsgemäßes Entweichen von Attributen), wird es angesichts der Verwendung der data-Attributverwendung von HTML5 zunehmend von größerem Interesse .

Adam Gent
quelle
9

org.apache.commons.lang3.StringEscapeUtils ist jetzt veraltet. Sie müssen jetzt org.apache.commons.text.StringEscapeUtils von verwenden

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

Die meisten Bibliotheken bieten alles, was sie können, einschließlich Hunderten von Symbolen und Tausenden von Nicht-ASCII-Zeichen, was in der UTF-8-Welt nicht das ist, was Sie wollen.

Wie Jeff Williams bemerkte, gibt es keine einzige Option für "Escape-HTML", sondern mehrere Kontexte.

Angenommen, Sie verwenden niemals nicht zitierte Attribute und denken daran, dass unterschiedliche Kontexte existieren, dann hat es meine eigene Version geschrieben:

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);
}

Erwägen Sie das Einfügen von Texten aus Gist ohne Zeilenlängenbeschränkung .

Miha_x64
quelle