2010-05-08 12 views
25

Escribo el JS para una aplicación de chat en la que estoy trabajando en mi tiempo libre, y necesito tener identificadores HTML que cambien según los datos enviados por el usuario. Esto es usualmente algo conceptualmente inestable que ni siquiera lo intentaría, pero esta vez no veo que tenga muchas opciones. Lo que necesito hacer entonces es escapar de la identificación HTML para asegurarme de que no permitirá XSS o romper HTML.Desinfección de la entrada del usuario antes de agregarla al DOM en Javascript

Aquí está el código:

var user_id = escape(id) 
var txt = '<div class="chut">'+ 
      '<div class="log" id="chut_'+user_id+'"></div>'+ 
      '<textarea id="chut_'+user_id+'_msg"></textarea>'+ 
      '<label for="chut_'+user_id+'_to">To:</label>'+ 
      '<input type="text" id="chut_'+user_id+'_to" value='+user_id+' readonly="readonly" />'+ 
      '<input type="submit" id="chut_'+user_id+'_send" value="Message"/>'+ 
      '</div>'; 

¿Cuál sería la mejor manera de escapar id para evitar cualquier tipo de problema antes mencionado? Como puede ver, en este momento estoy usando la función incorporada escape(), pero no estoy seguro de qué tan bueno se supone que se debe comparar con otras alternativas. En general, estoy acostumbrado a desinfectar las entradas antes de que entren en un nodo de texto, no en una identificación en sí misma.

+0

¿Cuál es exactamente la función de 'id'? – Tgr

+0

la identificación en este momento es cualquier cadena que representa un usuario.Lo uso para diferenciar estructuras similares de la página que pertenecen a diferentes usuarios. por esa lógica, MD5 o base64 podrían ser una buena espera para hacerlo, supongo. Simplemente no hay cosas integradas para eso. –

Respuesta

30

Nunca use escape(). No tiene nada que ver con la codificación HTML. Se parece más a la codificación de URL, pero ni siquiera es así. Es una codificación extraña no estándar disponible solo en JavaScript.

Si desea un codificador HTML, tendrá que escribirlo usted mismo ya que JavaScript no le proporciona uno. Por ejemplo:

function encodeHTML(s) { 
    return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/"/g, '&quot;'); 
} 

Sin embargo, mientras que esto es suficiente para poner su user_id en lugares como el input value, no es suficiente para id porque identificadores sólo pueden utilizar una selección limitada de caracteres. (Y % no está entre ellos, por lo escape() o incluso encodeURIComponent() no es bueno.)

Se podía inventar su propio esquema de codificación para poner los caracteres en un documento de identidad, por ejemplo:

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

Pero Todavía tengo un problema si se produce el mismo user_id dos veces. Y, para ser sincero, todo el asunto de tirar cadenas de HTML suele ser una mala idea. Utilice los métodos DOM en su lugar y conserve las referencias de JavaScript para cada elemento, de modo que no tenga que seguir llamando al getElementById, o preocupándose acerca de cómo se insertan cadenas arbitrarias en los ID.

por ejemplo.:

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

También es posible usar una función de conveniencia o marco JS para reducir la excesiva duración de la creación-set-anexa llamadas allí.

ETA:

estoy usando jQuery en este momento como un marco

OK, y luego considerar los accesos directos de creación de jQuery 1.4, por ejemplo .:

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

El problema que tengo ahora es que uso JSONP para agregar elementos y eventos a una página, por lo que no puedo saber si los elementos ya existir o no antes de mostrar un mensaje.

Puede mantener una búsqueda de user_id a nodos de elemento (u objetos de envoltura) en JavaScript, para salvar a poner esa información en el propio DOM, donde los personajes que pueden ir en un id se encuentran restringidas.

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

(El prefijo _map_ se debe a que los objetos JavaScript No bastante trabajo como un mapeo de cadenas arbitrarias. La cadena vacía y, en IE, algunos Object nombres de los miembros, lo confunden.)

+0

Estoy usando jQuery en este momento como marco, por lo que cualquier idea relacionada con eso puede ser útil. El problema que tengo ahora es que uso JSONP para agregar elementos y eventos a una página, por lo que no puedo saber si los elementos ya existen o no antes de mostrar un mensaje. Esto me hizo pensar que estoy obligado a usar el método de mierda que tengo para encontrar qué elementos seleccionar o agregar si ya no están allí. Debido a esto, no creo que su última sugerencia pueda funcionar, pero podría estar equivocado. El conjunto de caracteres restringidos me hace pensar que un MD5 de la identificación podría ser todo lo que puedo hacer. –

+0

Aunque podría agregar una suposición sobre los caracteres aceptados de los nombres de usuario, use expresiones regulares y termine con esto. –

7

Se podría utilizar un simple expresión regular para afirmar que el id sólo contiene caracteres permitidos, así:

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

Mi ejemplo permite sólo caracteres alfanuméricos, y cadenas de longitud 1 a 16, se debe cambiar para que coincida con el tipo de identificadores que utiliza.

Por cierto, en la línea 6, a la propiedad de valor le falta un par de comillas, un error fácil de realizar cuando se cita en dos niveles.

No puedo ver su flujo de datos real, dependiendo del contexto, es posible que este cheque no sea del todo necesario o que no sea suficiente. Para hacer una revisión de seguridad adecuada, necesitaríamos más información.

En general, acerca de las funciones integradas de escape o desinfección, no confíe ciegamente en ellas. Necesita saber exactamente lo que hacen, y debe establecer que eso es realmente lo que necesita. Si no es lo que necesita, el código es suyo, la mayoría de las veces una simple expresión regular como la que le di funciona muy bien.

1

Debe tomar precauciones adicionales cuando utilice datos proporcionados por el usuario en atributos HTML. Debido a que los atributos tienen muchos más vectores de ataque que la salida dentro de las etiquetas HTML.

La única manera de evitar los ataques XSS es codificar todo excepto los caracteres alfanuméricos. Escape a todos los caracteres con valores ASCII menores que 256 con el & #xHH; formato. Lo cual desafortunadamente puede causar problemas en su escenario, si está usando clases CSS y javascript para recuperar esos elementos.

OWASP tiene una buena descripción de cómo mitigar 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

12

Otro enfoque que me gusta es el uso de las capacidades de DOM nativos: http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript

+1

@BrandonMintern gist no funciona para mí. – cmcculloh

+1

Escribí esa publicación en el blog. Desafortunadamente, la técnica TL; DR dada en la parte superior no es apropiada para el uso en atributos HTML. Hay algunos otros enfoques hacia el final de la publicación que también escapan correctamente para los atributos HTML, a saber: http://shebang.brandonmintern.com/foolproof-html-escaping-in-javascript/#hack-3-more-efficient -catchall –

1

ya que el texto que son escaparse aparecerá en un atributo HTML, debe asegurarse de escapar no solo de las entidades HTML sino también de los atributos HTML:

var ESC_MAP = { 
    '&': '&amp;', 
    '<': '&lt;', 
    '>': '&gt;', 
    '"': '&quot;', 
    "'": '&#39;' 
}; 

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

Luego, su código de escape se convierte en var user_id = escapeHTML(id, true).

Para obtener más información, vea Foolproof HTML escaping in Javascript.

Cuestiones relacionadas