Escapando strings HTML com jQuery

Alguém sabe de uma maneira fácil de escaping HTML de seqüências de caracteres no jQuery ? Eu preciso ser capaz de passar uma seqüência arbitrária e ter escapado adequadamente para exibição em uma página HTML (impedindo ataques de injeção de JavaScript / HTML). Tenho certeza de que é possível estender o jQuery para fazer isso, mas não sei o suficiente sobre o framework no momento para realizar isso.

Como você está usando o jQuery , basta definir a propriedade de text do elemento:

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

Há também a solução do mustache.js

 var entityMap = { '&': '&', '< ': '<', '>': '>', '"': '"', "'": ''', '/': '/', '`': '`', '=': '=' }; function escapeHtml (string) { return String(string).replace(/[&<>"'`=\/]/g, function (s) { return entityMap[s]; }); } 
 $('
').text('This is fun & stuff').html(); // "This is fun & stuff"

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

Se você está escapando por HTML, há apenas três que eu posso pensar que seria realmente necessário:

 html.replace(/&/g, "&").replace(//g, ">"); 

Dependendo do seu caso de uso, você também pode precisar fazer coisas como " para " . Se a lista fosse grande o suficiente, eu usaria apenas uma matriz:

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

encodeURIComponent() só irá escaping para URLs, não para HTML.

Eu escrevi uma pequena function que faz isso. Só escapa " , & , < e > (mas normalmente é tudo o que você precisa). É um pouco mais elegante do que as soluções propostas anteriormente, pois usa apenas um .replace() para fazer toda a conversão. ( EDIT 2: Reduzida complexidade de código, tornando a function ainda menor e mais simples, se você está curioso sobre o código original, veja o final desta resposta.)

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

Este é um JavaScript simples, não é usado o jQuery.

Escapando / e ' também

Edite em resposta ao comentário do mklement .

A function acima pode ser facilmente expandida para include qualquer caractere. Para especificar mais caracteres para escaping, basta inseri-los na class de caracteres na expressão regular (ou seja, dentro de /[...]/g ) e como uma input no object chr . ( EDIT 2: Abreviada esta function também, da mesma forma.)

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

Observe o uso acima de ' para apóstrofo (a entidade simbólica ' pode ter sido usada em vez disso - é definida em XML, mas originalmente não foi incluída na especificação HTML e, portanto, não pode ser suportada por todos os navegadores. Veja: Artigo da Wikipédia sobre codificações de caracteres HTML ). Eu também lembro de ter lido em algum lugar que usar entidades decimais é mais amplamente suportado do que usar hexadecimal, mas não consigo encontrar a fonte para isso agora. (E não pode haver muitos navegadores por aí que não suportem as entidades hexadecimais.)

Nota: Adicionar / e ' à lista de caracteres de escape não é tão útil, pois eles não têm nenhum significado especial em HTML e não precisam ser escapados.

Função escapeHtml Original

EDIT 2: A function original usou uma variável ( chr ) para armazenar o object necessário para o retorno de chamada .replace() . Essa variável também precisava de uma function extra anônima para ampliá-la, tornando a function (desnecessariamente) um pouco maior e mais complexa.

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

Eu não testei quais das duas versões são mais rápidas. Se fizer isso, sinta-se à vontade para adicionar informações e links sobre isso aqui.

Fácil o suficiente para usar o sublinhado:

 _.escape(string) 

Sublinhado é uma biblioteca de utilitários que fornece muitos resources que o js nativo não fornece. Há também o lodash, que é a mesma API do sublinhado, mas foi reescrito para ter mais desempenho.

Eu percebo o quão tarde estou para esta festa, mas eu tenho uma solução muito fácil que não requer jQuery.

 escaped = new Option(unescaped).innerHTML; 

Edit: Isto não escapa citações. O único caso em que as cotações precisariam ser escapadas é se o conteúdo for colado em linha para um atributo dentro de uma cadeia HTML. É difícil para mim imaginar um caso em que isso seria um bom design.

Edit 2: Se o desempenho é crucial, a solução de maior desempenho (em cerca de 50%) ainda é uma série de substituições de regex. Navegadores modernos detectarão que as expressões regulares não contêm operadores, apenas uma cadeia de caracteres, e colapsam todas elas em uma única operação.

Aqui está uma function JavaScript limpa e clara. Ele irá escaping texto como “alguns

 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('
').text(str).html(); } // No jQuery, so use string replace. return str .replace(/&/g, '&') .replace(/>/g, '>') .replace(/

Tente Underscore.string lib, ele funciona com jQuery.

 _.str.escapeHTML('
Blah blah blah
')

saída:

 '<div>Blah blah blah</div>' 

Após os últimos testes, posso recomendar a solução JavaScript javaScript (DOM) mais rápida e completamente compatível com navegador :

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

Se você repeti-lo muitas vezes, pode fazê-lo com variables ​​preparadas uma vez:

 //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 } 

Veja minha comparação final de desempenho ( pergunta de pilha ).

Eu escapeHTML() o exemplo do moustache.js adicionando o método escapeHTML() ao object string.

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

Dessa forma, é muito fácil usar "Some , more Text&Text".escapeHTML()

escape() e unescape() são destinados a codificar / decodificar strings para URLs, não HTML.

Na verdade, eu uso o seguinte trecho para fazer o truque que não requer nenhum framework:

 var escapedHtml = html.replace(/&/g, '&') .replace(/>/g, '>') .replace(/ 

Se você tiver underscore.js, use _.escape (mais eficiente que o método jQuery postado acima):

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

Se você está indo a rota regex, há um erro no exemplo do tghw acima.

  var escaped = html; var findReplace = [[/&/g, "&"], [//g,">"], [/"/g, """]] for(var item in findReplace) { escaped = escaped.replace(item[0], item[1]); }  var escaped = html; var findReplace = [[/&/g, "&"], [//g, ">"], [/"/g, """]] for(var item in findReplace) { escaped = escaped.replace(findReplace[item[0]], findReplace[item[1]]); } 

Este é um bom exemplo seguro …

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

Você pode facilmente fazê-lo com baunilha js.

Basta adicionar um nó de texto ao documento. Será escapado pelo navegador.

 var escaped = document.createTextNode("") document.getElementById("[PARENT_NODE]").appendChild(escaped) 
 (function(undefined){ var charsToReplace = { '&': '&', '< ': '<', '>': '>' }; 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)); }; })(); 

Nenhuma variável global, alguma otimização de memory. Uso:

 "someand&symbol©".htmlEscape({'©': '©'}) 

O resultado é:

 "some<tag>and&symbol©" 
 function htmlEscape(str) { var stringval=""; $.each(str, function (i, element) { alert(element); stringval += element .replace(/&/g, '&') .replace(/"/g, '"') .replace(/'/g, ''') .replace(//g, '>') .replace(' ', '-') .replace('?', '-') .replace(':', '-') .replace('|', '-') .replace('.', '-'); }); alert(stringval); return String(stringval); } 

2 methods simples que requerem NO JQUERY …

Você pode codificar todos os caracteres em sua string assim:

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

Ou apenas direcione os personagens principais para se preocupar com & , quebras de linha, < , > , " e ' como:

 function encode(r){ return r.replace(/[\x26\x0A\<>'"]/g,function(r){return"&#"+r.charCodeAt(0)+";"}) } var myString='Encode HTML entities!\n"Safe" escape  
 

What JavaScript Generated:

What It Renders Too In HTML:

www.WHAK.com

Exemplo de escape JavaScript simples:

 function escapeHtml(text) { var div = document.createElement('div'); div.innerText = text; return div.innerHTML; } escapeHtml("") // "<script>alert('hi!');</script>" 
 function htmlDecode(t){ if (t) return $('
').html(t).text(); }

Funciona como um encanto

Esta resposta fornece os methods jQuery e JS normal, mas isso é mais curto sem usar o DOM:

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

Cadeia de escape: It%27s%20%3E%2020%25%20less%20complicated%20this%20way.

Se os espaços de escape te incomodarem, tente:

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

Escaped string: It%27s %3E 20%25 less complicated this way.

Infelizmente, a function escape() foi preterida na versão 1.5 do JavaScript . encodeURI() ou encodeURIComponent() são alternativas, mas eles ignoram ' , então a última linha de código se transformaria em:

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

Todos os principais navegadores ainda suportam o código curto, e dado o número de sites antigos, duvido que isso mude em breve.

Se você está salvando essas informações em um database , é errado escaping HTML usando um script do lado do cliente , isso deve ser feito no servidor . Caso contrário, é fácil ignorar sua proteção XSS.

Para deixar claro, aqui está um exemplo usando uma das respostas:

Vamos dizer que você está usando a function escapeHtml para escaping do Html de um comentário no seu blog e depois postá-lo no seu servidor.

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

O usuário poderia:

  • Edite os parâmetros de solicitação POST e substitua o comentário pelo código javascript.
  • Sobrescreva a function escapeHtml usando o console do navegador.

Se o usuário colar esse snippet no console, ele ignoraria a validação do XSS:

 function escapeHtml(string){ return string } 

Todas as soluções são inúteis se você não evitar a re-fuga, por exemplo, a maioria das soluções continuaria a escaping & para & .

 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] + ";"; } ) : ""; };