Méthode recommandée pour échapper au HTML en Java

262

Est - il un moyen recommandé pour échapper <, >, "et &caractères lors de la génération HTML dans le code Java simple? (À part faire manuellement ce qui suit, c'est-à-dire).

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
la source
2
Sachez que si vous effectuez une sortie dans un attribut HTML non cité, que d'autres caractères tels que l'espace, la tabulation, le retour arrière, etc. peuvent permettre aux attaquants d'introduire des attributs javascript sans aucun des caractères répertoriés. Voir la feuille de triche de prévention OWASP XSS pour plus.
Jeff Williams
BTW, dans ce code, vous devez échapper "&" avant "<" pour que cela fonctionne correctement ("& lt;" est remplacé par "& amp; lt;" sinon, qui est rendu par "& lt;" puis, pas "< "):source.replace("&", "&amp;").replace("<", "&lt;");
Tey '23

Réponses:

261

StringEscapeUtils d' 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);

Pour la version 3 :

import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
// ...
String escaped = escapeHtml4(source);
dfa
la source
2
Bien que ce StringEscapeUtilssoit bien, il n'échappera pas correctement aux espaces blancs pour les attributs si vous souhaitez éviter la normalisation des espaces blancs HTML / XML. Voir ma réponse pour plus de détails.
Adam Gent
21
L'exemple ci-dessus est cassé. Utilisez maintenant la méthode escapeHtml4 ().
stackoverflowuser2010
3
Pour les fans de Guava, voir la réponse d'Okranz ci-dessous.
George Hawkins
2
Si la page Web a un codage UTF-8, alors tout ce dont nous avons besoin est htmlEscaper de Guava qui n'échappe que les cinq caractères ASCII suivants: '"& <>. pages?
zdenekca
4
Il est désormais obsolète dans commons-lang3. Il a été déplacé vers commons.apache.org/proper/commons-text
Danny
137

Une alternative à Apache Commons: utilisez la méthode SpringHtmlUtils.htmlEscape(String input) .

Adamski
la source
9
Merci. Je l' ai utilisé ( au lieu de StringEscapeUtils.escapeHtml()partir de apache-commons2.6) parce qu'il laisse des caractères russes tel quel.
Slava Semushin
6
C'est bon à savoir. TBH J'accorde une large place aux trucs Apache ces jours-ci.
Adamski
1
Je l'ai aussi utilisé, il laisse les caractères chinois tels quels.
smartwjw
Comment se compare-t-il à l'alternative de goyave mentionnée ci-dessous?
vishvAs vAsuki
2
Et il code également l'apostrophe, il est donc utile, contrairement à apache StringEscapeUtils
David Balažic
58

Belle méthode courte:

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

Basé sur https://stackoverflow.com/a/8838023/1199155 (l'ampli y manque). Les quatre caractères cochés dans la clause if sont les seuls en dessous de 128, selon http://www.w3.org/TR/html4/sgml/entities.html

Bruno Eberhard
la source
Agréable. Il n'utilise pas les "versions html" des encodages (exemple: "á" serait "& aacute;" au lieu de "& # 225;"), mais comme les numériques fonctionnent même dans IE7, je suppose que non avoir à vous inquiéter. Merci.
nonzaprej
Pourquoi encodez-vous tous ces personnages lorsque l'OP a demandé à échapper aux 4 personnages concernés? Vous gaspillez du CPU et de la mémoire.
David Balažic
1
Vous avez oublié l'apostrophe. Les gens peuvent donc injecter des attributs non cotés partout où ce code est utilisé pour échapper aux valeurs d'attribut.
David Balažic
45

Il existe une version plus récente de la bibliothèque Apache Commons Lang et elle utilise un nom de package différent (org.apache.commons.lang3). Le StringEscapeUtilspossède maintenant différentes méthodes statiques pour échapper à différents types de documents ( http://commons.apache.org/proper/commons-lang/javadocs/api-3.0/index.html ). Donc, pour échapper à la chaîne 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
la source
3
Malheureusement, rien n'existe pour HTML 5, et les documents Apache ne précisent pas s'il convient d'utiliser escapeHtml4 pour HTML 5.
Paul Vincent Craven
43

Pour ceux qui utilisent 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);
okrasz
la source
40

Sur Android (API 16 ou supérieure), vous pouvez:

Html.escapeHtml(textToScape);

ou pour une API inférieure:

TextUtils.htmlEncode(textToScape);
OriolJ
la source
Y a-t-il une raison d'utiliser à la escapeHtmlplace de htmlEncode?
Muz
2
Voir aussi ma ma question sur la différence entre ces deux. (@Muz)
JonasCz
37

Soyez prudent avec cela. Il existe un certain nombre de «contextes» différents dans un document HTML: à l'intérieur d'un élément, valeur d'attribut citée, valeur d'attribut non cotée, attribut URL, javascript, CSS, etc ... Vous devrez utiliser une méthode de codage différente pour chacun des ceux-ci pour empêcher le Cross-Site Scripting (XSS). Consultez la feuille de triche de prévention OWASP XSS pour plus de détails sur chacun de ces contextes. Vous pouvez trouver des méthodes d'échappement pour chacun de ces contextes dans la bibliothèque OWASP ESAPI - https://github.com/ESAPI/esapi-java-legacy .

Jeff Williams
la source
6
MERCI d'avoir souligné que le contexte dans lequel vous souhaitez encoder la sortie est très important. Le terme "coder" est également un verbe beaucoup plus approprié que "échapper". Escape implique une sorte de hack spécial, par opposition à "comment puis-je encoder cette chaîne pour: un attribut XHTML / paramètre de requête SQL / chaîne d'impression PostScript / champ de sortie CSV?
Roboprog
5
«Encode» et «escape» sont tous deux largement utilisés pour décrire cela. Le terme "échappement" est généralement utilisé lorsque le processus consiste à ajouter un "caractère d'échappement" avant un caractère syntaxiquement pertinent, tel que l'échappement d'un caractère de citation avec une barre oblique inversée \ "Le terme" coder "est plus généralement utilisé lorsque vous traduisez un sous une autre forme, comme une URL codant le caractère de citation% 22 ou une entité HTML codant en & # x22 ou @quot.
Jeff Williams
1
Pour vous éviter de googler, recherchez la classe Encoder static.javadoc.io/org.owasp.esapi/esapi/2.0.1/org/owasp/esapi/…
Jakub Bochenski
14

À certaines fins, HtmlUtils :

import org.springframework.web.util.HtmlUtils;
[...]
HtmlUtils.htmlEscapeDecimal("&"); //gives &#38;
HtmlUtils.htmlEscape("&"); //gives &amp;
AUU
la source
1
D'après les commentaires de printemps de HtmlUtils: * <p> Pour un ensemble complet d'utilitaires d'échappement String, * considérez Apache Commons Lang et sa classe StringEscapeUtils. * Nous n'utilisons pas cette classe ici pour éviter une dépendance d'exécution * sur Commons Lang uniquement pour l'échappement HTML. De plus, l'échappement HTML * de Spring est plus flexible et 100% HTML 4.0 conforme. Si vous utilisez déjà Apache commons dans votre projet, vous devriez probablement utiliser les StringEscapeUtils d'apache
andreyro
10

Bien que la réponse @dfa de org.apache.commons.lang.StringEscapeUtils.escapeHtmlsoit agréable et que je l'ai utilisée dans le passé, elle ne devrait pas être utilisée pour échapper les attributs HTML (ou XML) sinon le blanc sera normalisé (ce qui signifie que tous les caractères adjacents deviennent un seul espace).

Je le sais parce que des bogues ont été déposés contre ma bibliothèque (JATL) pour les attributs où les espaces blancs n'étaient pas préservés. J'ai donc une classe drop (copy n 'paste) (dont j'en ai volé une partie de JDOM) qui différencie l'échappement des attributs et du contenu des éléments .

Bien que cela n'ait peut-être pas eu autant d'importance dans le passé (échappement correct des attributs), cela devient de plus en plus intéressant compte tenu de l'utilisation de l'utilisation des data-attributs HTML5 .

Adam Gent
la source
9

org.apache.commons.lang3.StringEscapeUtils est désormais obsolète. Vous devez maintenant utiliser org.apache.commons.text.StringEscapeUtils par

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

La plupart des bibliothèques proposent d'échapper à tout ce qu'elles peuvent, y compris des centaines de symboles et des milliers de caractères non ASCII, ce qui n'est pas ce que vous voulez dans le monde UTF-8.

De plus, comme l'a noté Jeff Williams, il n'y a pas une seule option «d'échappement HTML», il existe plusieurs contextes.

En supposant que vous n'utilisez jamais d'attributs non cités et en gardant à l'esprit que différents contextes existent, il a écrit ma propre version:

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

Envisagez de copier-coller à partir de Gist sans limite de longueur de ligne .

Miha_x64
la source