Metodo consigliato per sfuggire all'HTML in Java


262

Esiste un metodo consigliato per fuggire <, >, "e &caratteri quando l'output HTML in codice Java pianura? (Oltre a fare manualmente quanto segue, cioè).

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

2
Tieni presente che se stai inserendo un attributo HTML non quotato, che altri caratteri come spazio, tabulazione, backspace, ecc ... possono consentire agli aggressori di introdurre attributi javascript senza nessuno dei caratteri elencati. Per ulteriori informazioni, consultare il cheat sheet di OWASP XSS Prevention.
Jeff Williams,

A proposito, in questo codice, dovresti scappare "&" prima di "<" affinché funzioni correttamente ("& lt;" viene sostituito con "& amp; lt;" altrimenti, che viene visualizzato come "& lt;" quindi, non "< "):source.replace("&", "&amp;").replace("<", "&lt;");
Dal

Risposte:


261

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

Per la versione 3 :

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

2
Anche se StringEscapeUtilsè bello non sfuggirà correttamente agli spazi bianchi per gli attributi se si desidera evitare la normalizzazione degli spazi bianchi HTML / XML. Vedi la mia risposta per maggiori dettagli.
Adam Gent,

21
L'esempio sopra è rotto. Utilizzare il metodo escapeHtml4 () ora.
stackoverflowuser2010

3
Per i fan di Guava vedi la risposta di okranz di seguito.
George Hawkins,

2
Se la pagina web ha la codifica UTF-8, tutto ciò di cui abbiamo bisogno è l'htmlEscaper di Guava che sfugge solo ai seguenti cinque caratteri ASCII: '"& <>. L'escapeHtml () di Apache sostituisce anche i caratteri non ASCII inclusi gli accenti che sembrano inutili con il web UTF-8 pagine?
zdenekca,

4
Ora è deprecato in commons-lang3. È stato spostato su commons.apache.org/proper/commons-text
Danny il

137

Un'alternativa ad Apache Commons: utilizzare il metodo SpringHtmlUtils.htmlEscape(String input) .


9
Grazie. L'ho usato (anziché StringEscapeUtils.escapeHtml()dalla apache-commons2.6) perché lascia i personaggi russi così come sono.
Slava Semushin,

6
Buono a sapersi. TBH Al giorno d'oggi offro un ampio posto agli Apache.
Adamski,

1
L'ho usato anche, lascia i caratteri cinesi così come sono.
smartwjw,

Come si confronta con la guava alternativa menzionata di seguito?
vishvAs vAsuki,

2
E codifica anche l'apostrofo, quindi è effettivamente utile, a differenza dell'apache StringEscapeUtils
David Balažic,

57

Bel metodo corto:

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

Basato su https://stackoverflow.com/a/8838023/1199155 (l'amplificatore manca lì). I quattro caratteri controllati nella clausola if sono i soli sotto 128, secondo http://www.w3.org/TR/html4/sgml/entities.html


Bello. Non usa le "versioni html" delle codifiche (esempio: "á" sarebbe "& aacute;" invece di "& # 225;"), ma dal momento che quelle numeriche funzionano anche in IE7, credo di no devi preoccuparti. Grazie.
nonzaprej,

Perché codifichi tutti quei personaggi quando l'OP ha chiesto di sfuggire ai 4 personaggi rilevanti? Stai sprecando CPU e memoria.
David Balažic,

1
Hai dimenticato l'apostrofo. Quindi le persone possono iniettare attributi non quotati ovunque dove questo codice viene utilizzato per sfuggire ai valori degli attributi.
David Balažic,

45

Esiste una versione più recente della libreria Lang di Apache Commons e utilizza un nome di pacchetto diverso (org.apache.commons.lang3). L' StringEscapeUtilsora ha diversi metodi statici per sfuggire a diversi tipi di documenti ( http://commons.apache.org/proper/commons-lang/javadocs/api-3.0/index.html ). Quindi, per sfuggire alla stringa HTML versione 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
Sfortunatamente non esiste nulla per HTML 5, né i documenti Apache specificano se è corretto usare escapeHtml4 per HTML 5.
Paul Vincent Craven

43

Per coloro che utilizzano 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

Su Android (API 16 o successiva) puoi:

Html.escapeHtml(textToScape);

o per API inferiore:

TextUtils.htmlEncode(textToScape);

C'è qualche motivo per usare escapeHtmlinvece di htmlEncode?
Muz,

2
Vedi anche la mia domanda sulla differenza tra questi due. (@Muz)
JonasCz - Ripristina Monica il

37

Stai attento con questo. Esistono diversi 'contesti' all'interno di un documento HTML: All'interno di un elemento, valore di attributo citato, valore di attributo non quotato, attributo URL, javascript, CSS, ecc ... Dovrai utilizzare un metodo di codifica diverso per ciascuno di questi per prevenire Cross-Site Scripting (XSS). Controlla il foglio informativo sulla prevenzione di OWASP XSS per i dettagli su ciascuno di questi contesti. Puoi trovare i metodi di escape per ciascuno di questi contesti nella libreria ESAPI di OWASP - https://github.com/ESAPI/esapi-java-legacy .


6
GRAZIE per aver sottolineato che il contesto in cui si desidera codificare l'output è molto importante. Anche il termine "codificare" è un verbo molto più appropriato di "fuga". Escape implica una sorta di hack speciale, al contrario di "come posso codificare questa stringa per: un attributo XHTML / parametro di query SQL / stringa di stampa PostScript / campo di output CSV?
Roboprog

5
'Encode' e 'escape' sono entrambi ampiamente usati per descriverlo. Il termine "escape" viene generalmente utilizzato quando il processo prevede l'aggiunta di un "carattere di escape" prima di un carattere sintatticamente rilevante, come la fuga da un carattere di virgoletta con una barra rovesciata \ "Il termine" codifica "viene più comunemente utilizzato quando si traduce un carattere in una forma diversa, ad esempio URL che codifica il carattere di citazione% 22 o entità HTML che codifica come & # x22 o @quot.
Jeff Williams


1
Per risparmiarti su Google, cerca la classe Encoder static.javadoc.io/org.owasp.esapi/esapi/2.0.1/org/owasp/esapi/…
Jakub Bochenski

14

Per alcuni scopi, HtmlUtils :

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

1
Dai commenti HtmlUtils di primavera: * <p> Per un set completo di utility di escape String, * considera Apache Commons Lang e la sua classe StringEscapeUtils. * Non stiamo usando quella classe qui per evitare una dipendenza runtime * da Commons Lang solo per la fuga dell'HTML. Inoltre, l'escaping HTML * di Spring è più flessibile e conforme al 100% HTML 4.0. Se stai già usando i comuni di Apache nel tuo progetto, probabilmente dovresti usare StringEscapeUtils di apache
andreyro,

10

Mentre la risposta @dfa di org.apache.commons.lang.StringEscapeUtils.escapeHtmlè buona e l'ho usata in passato, non dovrebbe essere usata per sfuggire agli attributi HTML (o XML) altrimenti lo spazio bianco sarà normalizzato (nel senso che tutti i caratteri spazi bianchi adiacenti diventano un unico spazio).

Lo so perché ho avuto dei bug archiviati nella mia libreria (JATL) per attributi in cui gli spazi bianchi non venivano conservati. Quindi ho un calo nella classe (copia e incolla) (di cui ne ho rubati alcuni da JDOM) che differenzia la fuga di attributi e contenuto degli elementi .

Anche se questo potrebbe non essere stato così importante in passato (la corretta attribuzione dell'escape) è sempre più di grande interesse dato l'uso dell'uso dell'attributo HTML5 data-.


9

org.apache.commons.lang3.StringEscapeUtils è ora obsoleto. Ora devi usare org.apache.commons.text.StringEscapeUtils di

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

1

La maggior parte delle biblioteche offre la possibilità di sfuggire a tutto ciò che può, tra cui centinaia di simboli e migliaia di caratteri non ASCII che non è ciò che si desidera nel mondo UTF-8.

Inoltre, come ha osservato Jeff Williams, non esiste un'unica opzione "escape HTML", ci sono diversi contesti.

Supponendo che non usi mai attributi non quotati e tenendo presente che esistono contesti diversi, ha scritto la mia versione:

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

Prendi in considerazione la possibilità di copiare e incollare da Gist senza limite di lunghezza della linea .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.