Saneantes a input do usuário antes de adicioná-lo ao DOM em Javascript

Estou escrevendo o JS para um aplicativo de bate-papo em que estou trabalhando no meu tempo livre e preciso ter identificadores de HTML que mudam de acordo com os dados enviados pelo usuário. Isso geralmente é algo conceitualmente instável o suficiente para que eu nem tente fazê-lo, mas eu não me vejo tendo muita escolha desta vez. O que eu preciso fazer é escaping do código HTML para ter certeza de que ele não permitirá XSS ou quebra de HTML.

Aqui está o código:

var user_id = escape(id) var txt = '
'+ '
'+ ''+ ''+ ''+ ''+ '
';

Qual seria a melhor maneira de escaping do id para evitar qualquer tipo de problema mencionado acima? Como você pode ver, agora estou usando a function escape() interna, mas não tenho certeza de como isso deve ser comparado a outras alternativas. Eu sou mais usado para sanear a input antes de entrar em um nó de texto, não em um id.

Nunca use escape() . Não tem nada a ver com codificação HTML. É mais parecido com a codificação de URL, mas não é propriamente isso. É uma codificação não padrão bizarra disponível apenas em JavaScript.

Se você quiser um codificador HTML, terá que escrevê-lo você mesmo, pois o JavaScript não oferece um. Por exemplo:

 function encodeHTML(s) { return s.replace(/&/g, '&').replace(/ 

No entanto, enquanto isso é suficiente para colocar seu user_id em lugares como o input value , não é suficiente para o id porque os IDs podem usar apenas uma seleção limitada de caracteres. (E % não está entre eles, então escape() ou mesmo encodeURIComponent() não é bom.)

Você poderia inventar seu próprio esquema de codificação para colocar qualquer caractere em um ID, por exemplo:

 function encodeID(s) { if (s==='') return '_'; return s.replace(/[^a-zA-Z0-9.-]/g, function(match) { return '_'+match[0].charCodeAt(0).toString(16)+'_'; }); } 

Mas você ainda tem um problema se o mesmo user_id ocorrer duas vezes. E, para ser honesto, a coisa toda em jogar em HTML é geralmente uma má ideia. Use os methods DOM e mantenha as referências JavaScript em cada elemento, para que você não precise continuar chamando getElementById ou se preocupando com a maneira como as cadeias arbitrárias são inseridas nos IDs.

por exemplo.:

 function addChut(user_id) { var log= document.createElement('div'); log.className= 'log'; var textarea= document.createElement('textarea'); var input= document.createElement('input'); input.value= user_id; input.readonly= True; var button= document.createElement('input'); button.type= 'button'; button.value= 'Message'; var chut= document.createElement('div'); chut.className= 'chut'; chut.appendChild(log); chut.appendChild(textarea); chut.appendChild(input); chut.appendChild(button); document.getElementById('chuts').appendChild(chut); button.onclick= function() { alert('Send '+textarea.value+' to '+user_id); }; return chut; } 

Você também pode usar uma function de conveniência ou uma estrutura JS para reduzir a duração das chamadas create-set-appends.

ETA:

Estou usando o jQuery no momento como um framework

OK, considere os atalhos de criação do jQuery 1.4, por exemplo:

 var log= $('
', {className: 'log'}); var input= $('', {readOnly: true, val: user_id}); ...

O problema que tenho agora é que eu uso o JSONP para adicionar elementos e events a uma página, e por isso não posso saber se os elementos já existem ou não antes de mostrar uma mensagem.

Você pode manter uma pesquisa de user_id para nós de elementos (ou objects de invólucro) em JavaScript, para salvar essas informações no próprio DOM, onde os caracteres que podem ir em um id são restritos.

 var chut_lookup= {}; ... function getChut(user_id) { var key= '_map_'+user_id; if (key in chut_lookup) return chut_lookup[key]; return chut_lookup[key]= addChut(user_id); } 

(O prefixo _map_ é porque os objects JavaScript não funcionam como um mapeamento de sequências arbitrárias. A string vazia e, no IE, alguns nomes de membros do Object , confundem isso.)

Outra abordagem que eu gosto é usar os resources nativos do DOM: http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript

Você poderia usar uma expressão regular simples para afirmar que o id contém apenas caracteres permitidos, da seguinte forma:

 if(id.match(/^[0-9a-zA-Z]{1,16}$/)){ //The id is fine } else{ //The id is illegal } 

Meu exemplo permite apenas caracteres alfanuméricos e strings de comprimento 1 a 16, você deve alterá-lo para corresponder ao tipo de IDs que você usa.

By the way, na linha 6, a propriedade de valor está faltando um par de citações, um erro fácil de fazer quando você citar em dois níveis.

Não consigo ver seu stream de dados real, dependendo do contexto, essa verificação pode não ser necessária ou pode não ser suficiente. Para fazer uma análise de segurança adequada, precisaríamos de mais informações.

Em geral, sobre funções internas de escape ou higienização, não confie nelas cegamente. Você precisa saber exatamente o que eles fazem, e você precisa estabelecer que isso é realmente o que você precisa. Se não é o que você precisa, o código é seu, na maioria das vezes um simples regex whitelisting como o que eu dei a você funciona muito bem.

Você também pode usar isto:

 function sanitarize(string) { const map = { '&': '&', '< ': '<', '>': '>', '"': '"', "'": ''', "/": '/', }; const reg = /[&<>"'/]/ig; return string.replace(reg, (match)=>(map[match])); } 

A documentação do OWASP sugere mapeamento: https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet

Você precisa tomar precauções extras ao usar dados fornecidos pelo usuário em atributos HTML. Porque os atributos têm muito mais vetores de ataque do que a saída dentro de tags HTML.

A única maneira de evitar ataques XSS é codificar tudo, exceto caracteres alfanuméricos. Escape todos os caracteres com valores ASCII menores que 256 com o & # xHH; formato. Que infelizmente pode causar problemas no seu cenário, se você estiver usando classs CSS e javascript para buscar esses elementos.

OWASP tem uma boa descrição de como atenuar o atributo HTML XSS:

http://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.233_-_JavaScript_Escape_Before_Inserting_Untrusted_Data_into_HTML_JavaScript_Data_Values

Como o texto que você está escapando aparecerá em um atributo HTML, você deve ter certeza de que não apenas as entidades HTML, mas também os atributos HTML serão excluídos:

 var ESC_MAP = { '&': '&', '< ': '<', '>': '>', '"': '"', "'": ''' }; function escapeHTML(s, forAttribute) { return s.replace(forAttribute ? /[&<>'"]/g : /[&<>]/g, function(c) { return ESC_MAP[c]; }); } 

Então, seu código de escape se torna var user_id = escapeHTML(id, true) .

Para mais informações, consulte HTML escapado em JavaScript .