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;"); // ...

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

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


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


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

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

40

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

Html.escapeHtml(textToScape);

ou pour une API inférieure:

TextUtils.htmlEncode(textToScape);

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 .


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;

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 .


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>

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 .

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.