HtmlSpecialChars équivalent en Javascript?

167

Apparemment, c'est plus difficile à trouver que je ne le pensais. Et c'est même si simple ...

Existe-t-il une fonction équivalente aux htmlspecialchars de PHP intégrés en Javascript? Je sais qu'il est assez facile de l'implémenter vous-même, mais utiliser une fonction intégrée, si elle est disponible, est tout simplement plus agréable.

Pour ceux qui ne connaissent PHP, htmlspecialchars traduit des choses comme <htmltag/>dans&lt;htmltag/&gt;

Je le sais escape()et encodeURI()ne fonctionne pas de cette façon.

Bart van Heukelom
la source
php a de très bons outils, var_dump, print_r, htmlspecialchars etc. Malheureusement, je suppose que ce n'est pas la même chose avec js. js alert est si pauvre. Un moyen rapide de voir qu'une chaîne inattendue (et invisible dans la boîte d'alerte) arrive est d'alerter la longueur de la chaîne au lieu de la chaîne itslef.
Melsi
Possibilité de duplication des chaînes HTML d'échappement avec jQuery
nhahtdh
Voir stackoverflow.com/a/12034334/8804293 , il a une excellente réponse
Elijah Mock

Réponses:

330

Il y a un problème avec votre code de solution - il n'échappera qu'à la première occurrence de chaque caractère spécial. Par exemple:

escapeHtml('Kip\'s <b>evil</b> "test" code\'s here');
Actual:   Kip&#039;s &lt;b&gt;evil</b> &quot;test" code's here
Expected: Kip&#039;s &lt;b&gt;evil&lt;/b&gt; &quot;test&quot; code&#039;s here

Voici le code qui fonctionne correctement:

function escapeHtml(text) {
  return text
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;")
      .replace(/'/g, "&#039;");
}

Mettre à jour

Le code suivant produira des résultats identiques à ceux ci-dessus, mais il fonctionne mieux, en particulier sur de gros blocs de texte (merci jbo5112 ).

function escapeHtml(text) {
  var map = {
    '&': '&amp;',
    '<': '&lt;',
    '>': '&gt;',
    '"': '&quot;',
    "'": '&#039;'
  };
  
  return text.replace(/[&<>"']/g, function(m) { return map[m]; });
}
Kip
la source
5
la bonne chose à propos de cette fonction est qu'elle fonctionne dans node.js qui n'a pas de dom par défaut
booyaa
6
Il est plus rapide d'utiliser une seule fonction de remplacement et de mappage, et le remplacement unique s'adapte beaucoup mieux. ( jsperf.com/escape-html-special-chars/11 )
jbo5112
1
@ jbo5112 bon point, je ne savais pas que JS autorisait les rappels pour le remplacement. Ce code est plus facile à comprendre cependant, et je doute que raser quelques millisecondes de escapeHtml () fasse une différence à moins que vous ne l'appeliez des centaines de fois de suite pour une raison quelconque.
Kip
Cela déformera les URL dans le texte, ce qui les rendra inutilisables pour des plugins comme Autolinker.js . Y a-t-il un moyen d'aborder cela?
Radek Matěj
4
@ RadekMatěj Même dans ce cas, il est parfaitement valide (je dirais préférable) que les deux esperluettes soient encodées lorsqu'elles sont utilisées dans un document HTML. Je considérerais toujours cela comme un bug avec le plugin.
Kip le
31

C'est l'encodage HTML. Il n'y a pas de fonction javascript native pour faire cela, mais vous pouvez google et en obtenir des bien conçues.

Par exemple, http://sanzon.wordpress.com/2008/05/01/neat-little-html-encoding-trick-in-javascript/

EDIT:
C'est ce que j'ai testé:

var div = document.createElement('div');
  var text = document.createTextNode('<htmltag/>');
  div.appendChild(text);
  console.log(div.innerHTML);

Production: &lt;htmltag/&gt;

okw
la source
Dommage, je vais juste devoir utiliser une fonction personnalisée alors.
Bart van Heukelom
Vous pouvez essayer la méthode dans le lien que j'ai inclus dans mon message. Concept assez soigné en effet.
okw
@okw: Ok, d'abord vous avez lié à ceci: yuki-onna.co.uk/html/encode.html qui fait exactement ce que encodeURIComponentfait et pas du tout ce que l'OP a demandé. Alors pouvez-vous modifier s'il vous plaît? Je n'arrive pas à annuler mon -1.
Crescent Fresh
Yah, le code de cette page semble logique mais je ne l'ai pas testé. Le nouveau lien fonctionne bien, je l'ai vérifié moi-même. J'ai déjà mis à jour le message il y a quelque temps.
okw
@BeauCielBleu: Non. Les seuls nœuds créés sont un seul divélément et un nœud de texte. Créer un nœud de texte avec du texte `<img src = bogus onerror = alert (1337)>` créera simplement un nœud de texte, pas un imgélément.
Tim Down
26

Vaut le détour: http://bigdingus.com/2007/12/29/html-escaping-in-javascript/

escapeHTML: (function() {
 var MAP = {
   '&': '&amp;',
   '<': '&lt;',
   '>': '&gt;',
   '"': '&#34;',
   "'": '&#39;'
 };
  var repl = function(c) { return MAP[c]; };
  return function(s) {
    return s.replace(/[&<>'"]/g, repl);
  };
})()

Remarque : ne l'exécutez qu'une seule fois. Et ne l'exécutez pas sur des chaînes déjà encodées, par exemple &amp;devient&amp;amp;

Chris Jacob
la source
3
Cela devrait être la réponse acceptée et votée la plus élevée. Je ne sais pas pourquoi il n'y a pas eu de votes. Il s'agit du benchmarking le plus rapide avec à la fois une longue (résultat de recherche Google de 326 Ko) et une courte chaîne d'entrée sur jsperf ( jsperf.com/escape-html-special-chars/11 ). Veuillez voter pour cela.
jbo5112
Quelle est la différence entre celui-ci la réponse qui a obtenu le plus de votes ?. Pourquoi la fonction intérieure supplémentaire?. Une explication pourrait aider les utilisateurs à mieux comprendre
Kosem
19

Avec jQuery, cela peut être comme ceci:

var escapedValue = $('<div/>').text(value).html();

À partir d'une question connexe Échapper des chaînes HTML avec jQuery

Comme mentionné dans le commentaire, les guillemets doubles et simples sont laissés tels quels pour cette implémentation. Cela signifie que cette solution ne doit pas être utilisée si vous devez créer un attribut d'élément sous la forme d'une chaîne html brute.

Alexandre Yanovets
la source
2
une idée s'il y a une surcharge à cela - ajouter un objet factice au DOM?
Kip le
et y a-t-il d'autres avantages (par exemple, si vous avez des caractères Unicode ou quelque chose du genre)?
Kip le
4
Quelque chose que j'ai trouvé avec ceci: les guillemets doubles et les guillemets simples sont laissés tels quels. Cela rend cela problématique si vous souhaitez l'utiliser dans une valeur d'attribut.
Kip le
1
Pour de petits morceaux de texte, cela prend 30 fois plus longtemps que l'exécution de tous les remplacements. Il évolue cependant mieux. Avec quelque chose d'aussi gigantesque qu'une page de résultats de recherche Google (326 Ko), c'est 25 à 30% plus rapide que les remplacements ou le faire en javascript. Cependant, ils perdent tous systématiquement face à un seul remplacement et à une fonction de mappage.
jbo5112
4
comment les gens votent pour cette réponse: la réponse a jquery: +1 - n'échappe PAS aux guillemets simples et doubles: ummmm .. (tête grattée) .. +1. <!-- Caps rage begin --> Cette réponse doit avoir un score NÉGATIF ​​car elle ne se rapproche même pas de la réponse à la question «équivalent HtmlSpecialChars». <!-- Caps rage end -->il-n'échappe pas aux-citations-jésus-christ-et-autres-divinités. OMG vous jquery les gens.
Sharky
19

Voici une fonction pour échapper au HTML:

function escapeHtml(str)
{
    var map =
    {
        '&': '&amp;',
        '<': '&lt;',
        '>': '&gt;',
        '"': '&quot;',
        "'": '&#039;'
    };
    return str.replace(/[&<>"']/g, function(m) {return map[m];});
}

Et pour décoder:

function decodeHtml(str)
{
    var map =
    {
        '&amp;': '&',
        '&lt;': '<',
        '&gt;': '>',
        '&quot;': '"',
        '&#039;': "'"
    };
    return str.replace(/&amp;|&lt;|&gt;|&quot;|&#039;/g, function(m) {return map[m];});
}
Dan Bray
la source
6

Underscore.js fournit une fonction pour cela:

_.escape(string)

Échappe une chaîne à insérer dans HTML, en remplaçant les caractères &, <,>, "et '.

http://underscorejs.org/#escape

Ce n'est pas une fonction Javascript intégrée, mais si vous utilisez déjà Underscore, c'est une meilleure alternative que d'écrire votre propre fonction si vos chaînes à convertir ne sont pas trop volumineuses.

mer10z_tech
la source
5

Une autre solution consiste à renoncer complètement à la correspondance des caractères et à convertir à la place tous les caractères indésirables en leurs références de caractères numériques respectives, par exemple:

function escapeHtml(raw) {
    return raw.replace(/[&<>"']/g, function onReplace(match) {
        return '&#' + match.charCodeAt(0) + ';';
    });
}

Notez que le RegEx spécifié ne gère que les caractères spécifiques que l'OP voulait échapper mais, selon le contexte dans lequel le HTML échappé va être utilisé, ces caractères peuvent ne pas être suffisants. L'article de Ryan Grove Il y a plus à échapper HTML que &, <,> et " est une bonne lecture sur le sujet. Et selon votre contexte, le RegEx suivant peut très bien être nécessaire pour éviter l'injection XSS:

var regex = /[&<>"'` !@$%()=+{}[\]]/g
Fredric
la source
3
String.prototype.escapeHTML = function() {
        return this.replace(/&/g, "&amp;")
                   .replace(/</g, "&lt;")
                   .replace(/>/g, "&gt;")
                   .replace(/"/g, "&quot;")
                   .replace(/'/g, "&#039;");
    }

échantillon :

var toto = "test<br>";
alert(toto.escapeHTML());
patrick
la source
3

Il y a de fortes chances que vous n'ayez pas besoin d'une telle fonction. Puisque votre code est déjà dans le navigateur *, vous pouvez accéder directement au DOM au lieu de générer et encoder du HTML qui devra être décodé à l'envers par le navigateur pour être réellement utilisé.

Utilisez la innerTextpropriété pour insérer du texte brut dans le DOM en toute sécurité et beaucoup plus rapidement que d'utiliser l'une des fonctions d'échappement présentées. Encore plus rapide que d'attribuer une chaîne préencodée statique à innerHTML.

Utilisez classListpour éditer des classes, datasetpour définir des data-attributs et setAttributepour d'autres.

Tous ces éléments géreront votre fuite. Plus précisément, aucun échappement n'est nécessaire et aucun encodage ne sera effectué sous **, puisque vous travaillez autour du HTML, la représentation textuelle de DOM.

// use existing element
var author = 'John "Superman" Doe <[email protected]>';
var el = document.getElementById('first');
el.dataset.author = author;
el.textContent = 'Author: '+author;

// or create a new element
var a = document.createElement('a');
a.classList.add('important');
a.href = '/search?q=term+"exact"&n=50';
a.textContent = 'Search for "exact" term';
document.body.appendChild(a);

// actual HTML code
console.log(el.outerHTML);
console.log(a.outerHTML);
.important { color: red; }
<div id="first"></div>

* Cette réponse n'est pas destinée aux utilisateurs JavaScript côté serveur (Node.js, etc. )

** Sauf si vous le convertissez explicitement en HTML réel par la suite. Par exemple en accédant innerHTML- c'est ce qui se passe lorsque vous exécutez $('<div/>').text(value).html();suggéré dans d'autres réponses. Donc, si votre objectif final est d'insérer des données dans le document, en procédant de cette façon, vous ferez le travail deux fois. Vous pouvez également voir que dans le HTML résultant, tout n'est pas encodé, seulement le minimum nécessaire pour qu'il soit valide. Cela est fait en fonction du contexte, c'est pourquoi cette méthode jQuery n'encode pas les guillemets et ne doit donc pas être utilisée comme un escaper à usage général. L'échappement des guillemets est nécessaire lorsque vous créez du HTML sous forme de chaîne avec des données non approuvées ou contenant des guillemets à la place de la valeur d'un attribut. Si vous utilisez l'API DOM, vous n'avez pas du tout à vous soucier de vous échapper.

utilisateur
la source
Merci pour cela! J'ai passé beaucoup trop de temps à chercher une solution aussi simple. Une chose importante que j'ai découverte est que si votre texte contient des nouvelles lignes, vous devrez les remplacer par des sauts de ligne HTML (quelque chose comme el.textContent = str; el.innerHTML = el.innerHTML.replace(/\n/g, '<br>')), ou définir la white-spacepropriété CSS sur preoupre-wrap
stellatedHexahedron
@stellatedHexahedron, merci d'avoir soulevé ce problème. J'ai changé ma réponse pour recommander innerTextau lieu de textContent. Bien qu'un peu plus lent et présente d' autres différences lors de la lecture de la propriété, il est plus intuitif en ce sens qu'il effectue le <br>remplacement automatiquement lors de son affectation.
utilisateur
2

Pour les utilisateurs de Node.JS (ou les utilisateurs utilisant le runtime Jade dans le navigateur), vous pouvez utiliser la fonction d'échappement de Jade.

require('jade').runtime.escape(...);

Cela n'a aucun sens de l'écrire vous-même si quelqu'un d'autre le maintient. :)

BMiner
la source
1

Je développe un peu la réponse d'okw.

Vous pouvez utiliser les fonctions DOM du navigateur pour cela.

var utils = {
    dummy: document.createElement('div'),
    escapeHTML: function(s) {
        this.dummy.textContent = s
        return this.dummy.innerHTML
    }
}

utils.escapeHTML('<escapeThis>&')

Cela renvoie &lt;escapeThis&gt;&amp;

Il utilise la fonction standard createElementpour créer un élément invisible, puis utilise la fonction textContentpour définir une chaîne comme son contenu, puis innerHTMLpour obtenir le contenu dans sa représentation HTML.

Jonas Eberle
la source
0
function htmlspecialchars(str) {
 if (typeof(str) == "string") {
  str = str.replace(/&/g, "&amp;"); /* must do &amp; first */
  str = str.replace(/"/g, "&quot;");
  str = str.replace(/'/g, "&#039;");
  str = str.replace(/</g, "&lt;");
  str = str.replace(/>/g, "&gt;");
  }
 return str;
 }

la source
0

J'espère que cela gagnera la course en raison de ses performances et surtout pas d'une logique chaînée utilisant .replace ('&', '&'). Replace ('<', '<') ...

var mapObj = {
   '&':"&amp;",
   '<':"&lt;",
   '>':"&gt;",
   '"':"&quot;",
   '\'':"&#039;"
};
var re = new RegExp(Object.keys(mapObj).join("|"),"gi");

function escapeHtml(str) 
{   
    return str.replace(re, function(matched)
    {
        return mapObj[matched.toLowerCase()];
    });
}

console.log('<script type="text/javascript">alert('Hello World');</script>');
console.log(escapeHtml('<script type="text/javascript">alert('Hello World');</script>'));
Aéré
la source
0

Inversé un:

function decodeHtml(text) {
    return text
        .replace(/&amp;/g, '&')
        .replace(/&lt;/ , '<')
        .replace(/&gt;/, '>')
        .replace(/&quot;/g,'"')
        .replace(/&#039;/g,"'");
}
Gleb Dolzikov
la source
La question n'est pas de savoir comment décoder les entités. Cela fait le contraire de ce que demande la question.
Quentin
Cela ne remplacera que les premières instances de &lt;et &gr;dans une chaîne.
Quentin
Cela ne décodera que les cinq caractères qui (en dehors des documents non Unicode) doivent être échappés, il ne décodera pas ceux qui peuvent être échappés.
Quentin
Cela ne prend pas en compte les règles lorsque le point-virgule est facultatif.
Quentin
Si le HTML dit:, To write a greater than sign in HTML type &amp;gt;il s'affichera incorrectement à la >place de&gt;
Quentin
0

OWASP recommande que "[e] xscepté pour les caractères alphanumériques, [vous devriez] échapper tous les caractères avec des valeurs ASCII inférieures à 256 avec le &#xHH;format (ou une entité nommée si disponible) pour empêcher le changement de [un] attribut."

Voici donc une fonction qui fait cela, avec un exemple d'utilisation:

function escapeHTML(unsafe) {
  return unsafe.replace(
    /[\u0000-\u002F]|[\u003A-\u0040]|[\u005B-\u00FF]/g,
    c => '&#' + ('000' + c.charCodeAt(0)).substr(-4, 4) + ';'
  )
}
document.querySelector('div').innerHTML =
  '<span class=' +
  escapeHTML('this should break it! " | / % * + , - / ; < = > ^') +
  '>' +
  escapeHTML('<script>alert("inspect the attributes")\u003C/script>') +
  '</span>'
<div></div>

ADJenks
la source
-1
function htmlEscape(str){
    return str.replace(/[&<>'"]/g,x=>'&#'+x.charCodeAt(0)+';')
}

Cette solution utilise le code numérique des caractères, par exemple <est remplacé par&#60; .

Bien que ses performances soient légèrement inférieures à celles de la solution utilisant une carte , elle présente les avantages:

  • Ne dépend pas d'une bibliothèque ou d'un DOM
  • Assez facile à retenir (vous n'avez pas besoin de mémoriser les 5 caractères d'échappement HTML)
  • Petit code
  • Raisonnablement rapide (c'est toujours plus rapide que 5 remplacements enchaînés)
user202729
la source