Échapper des chaînes HTML avec jQuery

Est-ce que quelqu'un sait d'un moyen simple d'échapper au HTML à partir de chaînes dans jQuery ? Je dois pouvoir passer une chaîne arbitraire et l'avoir échappé correctement pour l'afficher dans une page HTML (prévention des attaques d'injection JavaScript / HTML). Je suis sûr qu'il est possible d'étendre jQuery pour faire cela, mais je ne connais pas assez le cadre pour l'accomplir.

Puisque vous utilisez jQuery, vous pouvez simplement définir la propriété de text l'élément:

 // before: // <div class="someClass">text</div> var someHtmlString = "<script>alert('hi!');</script>"; // set a DIV's text: $("div.someClass").text(someHtmlString); // after: // <div class="someClass">&lt;script&gt;alert('hi!');&lt;/script&gt;</div> // get the text in a string: var escaped = $("<div>").text(someHtmlString).html(); // value: // &lt;script&gt;alert('hi!');&lt;/script&gt; 

Il y a aussi la solution de la moustache.js

 var entityMap = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;', '/': '&#x2F;', '`': '&#x60;', '=': '&#x3D;' }; function escapeHtml (string) { return String(string).replace(/[&<>"'`=\/]/g, function (s) { return entityMap[s]; }); } 
 $('<div/>').text('This is fun & stuff').html(); // "This is fun &amp; stuff" 

Source: http://debuggable.com/posts/encode-html-entities-with-jquery:480f4dd6-13cc-4ce9-8071-4710cbdd56cb

Si vous échappez au HTML, il n'y a que trois que je puisse penser que cela serait vraiment nécessaire:

 html.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;"); 

Selon votre cas d'utilisation, vous devrez peut-être faire des choses comme " à &quot; . Si la liste était assez grande, j'utiliserais simplement un tableau:

 var escaped = html; var findReplace = [[/&/g, "&amp;"], [/</g, "&lt;"], [/>/g, "&gt;"], [/"/g, "&quot;"]] for(var item in findReplace) escaped = escaped.replace(findReplace[item][0], findReplace[item][1]); 

encodeURIComponent() ne l'échappera que pour les URL, pas pour HTML.

J'ai écrit une petite fonction qui fait cela. Il échappe simplement à " , & , < et > (mais généralement c'est tout ce dont vous avez besoin de toute façon). Il est légèrement plus élégant que les solutions proposées précédemment, car il utilise uniquement un .replace() pour effectuer toute la conversion ( EDIT 2: Réduction de la complexité du code rendant la fonction encore plus petite et plus propre, si vous êtes curieux du code original, voir la fin de cette réponse.)

 function escapeHtml(text) { 'use strict'; return text.replace(/[\"&<>]/g, function (a) { return { '"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;' }[a]; }); } 

C'est un Javascript simple, aucun jQuery n'a été utilisé.

Échapper / et ' aussi

Modifier en réponse au commentaire de mklement .

La fonction ci-dessus peut être facilement développée pour inclure tout caractère. Pour spécifier plus de caractères à échapper, il suffit de les insérer dans la classe de caractères dans l'expression régulière (c.-à-d. Dans /[...]/g ) et comme entrée dans l'objet chr . ( EDIT 2: raccourcie cette fonction aussi, de la même manière).

 function escapeHtml(text) { 'use strict'; return text.replace(/[\"&'\/<>]/g, function (a) { return { '"': '&quot;', '&': '&amp;', "'": '&#39;', '/': '&#47;', '<': '&lt;', '>': '&gt;' }[a]; }); } 

Notez l'utilisation ci-dessus de &#39; Pour l'apostrophe (l'entité symbolique a pu être utilisée à la place – elle est définie dans XML, mais n'était pas incluse dans la spécification HTML et ne pourrait donc pas être prise en charge par tous les navigateurs. Voir: article de Wikipédia sur les encodages de caractères HTML ). Je me souviens aussi de lire quelque part que l'utilisation d'entités décimales est plus largement soutenue que l'utilisation d'hexadécimal, mais je ne peux pas trouver la source pour cela maintenant. (Et il ne peut y avoir de nombreux navigateurs qui ne prennent pas en charge les entités hexadécimales.)

Remarque: Ajouter / et ' à la liste des caractères échappés n'est pas si utile, car ils n'ont aucune signification particulière en HTML et n'ont pas besoin d'être échappés.

Fonction escapeHtml originale

EDIT 2: La fonction d'origine a utilisé une variable ( chr ) pour stocker l'objet nécessaire pour le rappel de .replace() . Cette variable nécessitait également une fonction anonyme supplémentaire pour l'étendre, rendant la fonction (inutilement) un peu plus grande et plus complexe.

 var escapeHtml = (function () { 'use strict'; var chr = { '"': '&quot;', '&': '&amp;', '<': '&lt;', '>': '&gt;' }; return function (text) { return text.replace(/[\"&<>]/g, function (a) { return chr[a]; }); }; }()); 

Je n'ai pas testé laquelle des deux versions est plus rapide. Si vous le faites, n'hésitez pas à ajouter des informations et des liens à ce sujet ici.

Facile d'utiliser le trait de soulignement:

 _.escape(string) 

Underscore est une bibliothèque d'utilitaires qui fournit beaucoup de fonctionnalités que js natif ne fournit pas. Il y a aussi lodash qui est la même API que le trait de soulignement, mais a été réécrit pour être plus performant.

Voici une fonction JavaScript claire et claire. Il va échapper au texte tel que "quelques <beaucoup" dans "quelques-uns" beaucoup ".

 function escapeHtmlEntities (str) { if (typeof jQuery !== 'undefined') { // Create an empty div to use as a container, // then put the raw text in and get the HTML // equivalent out. return jQuery('<div/>').text(str).html(); } // No jQuery, so use string replace. return str .replace(/&/g, '&amp;') .replace(/>/g, '&gt;') .replace(/</g, '&lt;') .replace(/"/g, '&quot;'); } 

Essayez Underscore.string lib, ça marche avec jQuery.

 _.str.escapeHTML('<div>Blah blah blah</div>') 

sortie:

 '&lt;div&gt;Blah blah blah&lt;/div&gt;' 

Après les derniers tests, je peux vous recommander la solution de script Java Java (DOM) la plus rapide et la plus complète du navigateur:

 function HTMLescape(html){ return document.createElement('div') .appendChild(document.createTextNode(html)) .parentNode .innerHTML } 

Si vous le répétez plusieurs fois, vous pouvez le faire avec une fois les variables préparées:

 //prepare variables var DOMtext = document.createTextNode("test"); var DOMnative = document.createElement("span"); DOMnative.appendChild(DOMtext); //main work for each case function HTMLescape(html){ DOMtext.nodeValue = html; return DOMnative.innerHTML } 

Regardez ma dernière comparaison de performance ( question de pile ).

Je me rends compte à quel point je suis arrivé à cette fête, mais j'ai une solution très simple qui ne nécessite pas jQuery.

 escaped = new Option(unescaped).innerHTML; 

Modifier: ceci n'échappe pas aux guillemets. Le seul cas où les citations devraient être échappées est si le contenu va être collé en ligne à un attribut dans une chaîne HTML. Il est difficile pour moi d'imaginer un cas où ce serait un bon design.

Edition 2: si la performance est cruciale, la solution de performance la plus élevée (environ 50%) est encore une série de remplacements Regex. Les navigateurs modernes détecteront que les expressions régulières ne contiennent aucun opérateur, juste une chaîne, et l'effondrent toutes en une seule opération.

J'ai amélioré l'exemple de la moustache.js en ajoutant la méthode escapeHTML() à l'objet chaîne.

 var __entityMap = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': '&quot;', "'": '&#39;', "/": '&#x2F;' }; String.prototype.escapeHTML = function() { return String(this).replace(/[&<>"'\/]/g, function (s) { return __entityMap[s]; }); } 

De cette façon, il est assez facile d'utiliser "Some <text>, more Text&Text".escapeHTML()

escape() et unescape() sont destinés à coder / décoder des chaînes pour les URL, et non HTML.

En fait, j'utilise l'extrait suivant pour faire l'astuce qui ne nécessite aucun cadre:

 var escapedHtml = html.replace(/&/g, '&amp;') .replace(/>/g, '&gt;') .replace(/</g, '&lt;') .replace(/"/g, '&quot;') .replace(/'/g, '&apos;'); 

Si vous avez souligné.js, utilisez _.escape (plus efficace que la méthode jQuery ci-dessus):

 _.escape('Curly, Larry & Moe'); // returns: Curly, Larry &amp; Moe 

Si vous parcourez la route regex, il y a une erreur dans l'exemple ci-dessus.

 <!-- WON'T WORK - item[0] is an index, not an item --> var escaped = html; var findReplace = [[/&/g, "&amp;"], [/</g, "&lt;"], [/>/g,"&gt;"], [/"/g, "&quot;"]] for(var item in findReplace) { escaped = escaped.replace(item[0], item[1]); } <!-- WORKS - findReplace[item[]] correctly references contents --> var escaped = html; var findReplace = [[/&/g, "&amp;"], [/</g, "&lt;"], [/>/g, "&gt;"], [/"/g, "&quot;"]] for(var item in findReplace) { escaped = escaped.replace(findReplace[item[0]], findReplace[item[1]]); } 

C'est un bon exemple sûr …

 function escapeHtml(str) { if (typeof(str) == "string"){ try{ var newStr = ""; var nextCode = 0; for (var i = 0;i < str.length;i++){ nextCode = str.charCodeAt(i); if (nextCode > 0 && nextCode < 128){ newStr += "&#"+nextCode+";"; } else{ newStr += "?"; } } return newStr; } catch(err){ } } else{ return str; } } 
 (function(undefined){ var charsToReplace = { '&': '&amp;', '<': '&lt;', '>': '&gt;' }; var replaceReg = new RegExp("[" + Object.keys(charsToReplace).join("") + "]", "g"); var replaceFn = function(tag){ return charsToReplace[tag] || tag; }; var replaceRegF = function(replaceMap) { return (new RegExp("[" + Object.keys(charsToReplace).concat(Object.keys(replaceMap)).join("") + "]", "gi")); }; var replaceFnF = function(replaceMap) { return function(tag){ return replaceMap[tag] || charsToReplace[tag] || tag; }; }; String.prototype.htmlEscape = function(replaceMap) { if (replaceMap === undefined) return this.replace(replaceReg, replaceFn); return this.replace(replaceRegF(replaceMap), replaceFnF(replaceMap)); }; })(); 

Pas de variables globales, une certaine optimisation de la mémoire. Usage:

 "some<tag>and&symbol©".htmlEscape({'©': '&copy;'}) 

Le résultat est:

 "some&lt;tag&gt;and&amp;symbol&copy;" 

Vous pouvez facilement le faire avec vanille js.

Il suffit d'ajouter un noeud de texte au document. Il sera échappé par le navigateur.

 var escaped = document.createTextNode("<HTML TO/ESCAPE/>") document.getElementById("[PARENT_NODE]").appendChild(escaped) 
 function htmlEscape(str) { var stringval=""; $.each(str, function (i, element) { alert(element); stringval += element .replace(/&/g, '&amp;') .replace(/"/g, '&quot;') .replace(/'/g, '&#39;') .replace(/</g, '&lt;') .replace(/>/g, '&gt;') .replace(' ', '-') .replace('?', '-') .replace(':', '-') .replace('|', '-') .replace('.', '-'); }); alert(stringval); return String(stringval); } 

2 méthodes simples qui ne nécessitent PAS DE JQUERY …

Vous pouvez coder tous les caractères de votre chaîne comme ceci:

 function encode(e){return e.replace(/[^]/g,function(e){return"&#"+e.charCodeAt(0)+";"})} 

Ou cibler uniquement les personnages principaux pour s'inquiéter de & , les sauts de ligne, < , > et ' comme ' :

 function encode(r){ return r.replace(/[\x26\x0A\<>'"]/g,function(r){return"&#"+r.charCodeAt(0)+";"}) } var myString='Encode HTML entities!\n"Safe" escape <script></'+'script> & other tags!'; test.value=encode(myString); testing.innerHTML=encode(myString); /************* * \x26 is &ampersand (it has to be first), * \x0A is newline, *************/ 
 <p><b>What JavaScript Generated:</b></p> <textarea id=test rows="3" cols="55"></textarea> <p><b>What It Renders Too In HTML:</b></p> <div id="testing">www.WHAK.com</div> 
 function htmlDecode(t){ if (t) return $('<div />').html(t).text(); } 

Fonctionne comme un charme

Cette réponse fournit les méthodes jQuery et JS normales, mais ceci est le plus court sans utiliser le DOM:

 unescape(escape("It's > 20% less complicated this way.")) 

Chaîne It%27s%20%3E%2020%25%20less%20complicated%20this%20way.

Si les espaces échappés vous dérangent, essayez:

 unescape(escape("It's > 20% less complicated this way.").replace(/%20/g, " ")) 

Chaîne échouée: It%27s %3E 20%25 less complicated this way.

Malheureusement, la fonction escape() était obsolète dans JavaScript version 1.5 . encodeURI() ou encodeURIComponent() sont des alternatives, mais ils ignorent ' , alors la dernière ligne de code se transformerait en ceci:

 decodeURI(encodeURI("It's > 20% less complicated this way.").replace(/%20/g, " ").replace("'", '%27')) 

Tous les principaux navigateurs supportent toujours le code court, et compte tenu du nombre d'anciens sites Web, je doute que cela changera bientôt.

Si vous enregistrez ces informations dans une base de données , il est faux d'échapper à HTML en utilisant un script côté client , cela devrait être fait dans le serveur . Sinon, il est facile de contourner votre protection XSS.

Pour rendre mon point clair, voici un exemple utilisant l'une des réponses:

Disons que vous utilisez la fonction escapeHtml pour échapper au Html à partir d'un commentaire dans votre blog, puis le poster sur votre serveur.

 var entityMap = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': '&quot;', "'": '&#39;', "/": '&#x2F;' }; function escapeHtml(string) { return String(string).replace(/[&<>"'\/]/g, function (s) { return entityMap[s]; }); } 

L'utilisateur pourrait:

  • Modifiez les paramètres de demande POST et remplacez le commentaire par un code javascript.
  • Ecraser la fonction escapeHtml à l'aide de la console du navigateur.

Si l'utilisateur collez cet extrait dans la console, il contournerait la validation XSS:

 function escapeHtml(string){ return string } 

Toutes les solutions sont inutiles si vous n'empêchez pas de vous ré-échapper, par exemple, la plupart des solutions continueraient à échapper & à &amp; .

 escapeHtml = function (s) { return s ? s.replace( /[&<>'"]/g, function (c, offset, str) { if (c === "&") { var substr = str.substring(offset, offset + 6); if (/&(amp|lt|gt|apos|quot);/.test(substr)) { // already escaped, do not re-escape return c; } } return "&" + { "&": "amp", "<": "lt", ">": "gt", "'": "apos", '"': "quot" }[c] + ";"; } ) : ""; };