En la mayoría de los casos, cuando queremos manipular el contenido de una cadena, podemos preferir montarlo en el árbol DOM y realizar la manipulación en lugar de usar un reemplazo de expresión regular complicado, como:
Sin embargo, este enfoque siempre conduce a un problema XSS cuando el contenido externo contiene algunos scripts inesperados como el siguiente fragmento:
const unsafe = '<img src=x onerror=alert(1)>'; $('<div>').html(unsafe).find('img').remove(); // leads to a XSS const div = document.createElement('div'); div.innerHTML = unsafe; // leads to XSS const img = div.querySelector('img'); img && img.parentNode.removeChild(img);Entonces, ¿cómo podemos resolver este problema?
¿Volver a una expresión regular complicada? Para evitar este problema, la mayoría de los desarrolladores pueden usar algunas bibliotecas como js-xss o DOMPurify para manejar expresiones regulares complicadas para ellos. Sin embargo, estas bibliotecas a veces son demasiado estrictas para eliminar algunas etiquetas seguras, excepto si puede establecer las configuraciones correctas.
Aquí, solo quiero aclarar otra forma simple en la que podemos confiar en las técnicas de iframe sandbox, que también pueden prohibir a los navegadores acceder a los recursos del contenido, como cargar imágenes:
const unsafe = '<img src=x onerror=alert(1)>'; const $frame = $('<iframe>').appendTo('body'), $sandboxDoc = $frame.contents(); $frame.remove(); // scripts have been blocked // the document won't also access the image which throws us a 404 $('<div>', $sandboxDoc).html(unsafe).find('img').remove(); // by pure javascript const frame = document.createElement('iframe'); document.body.appendChild(frame); const sandboxDoc = frame.contentDocument; frame.parentNode.removeChild(frame); const div = sandboxDoc.createElement('div'); div.innerHTML = unsafe; const img = div.querySelector('img'); img && img.parentNode.removeChild(img);Tal documento todavía tiene un efecto secundario en el que los nodos DOM no calcularán ningún estilo:
$('<img style="cursor: zoom-in">', $sandboxDoc).css('cursor'); // => '' // by pure javascript const img = sandboxDoc.createElement('img'); img.setAttribute('style', 'cursor:zoom-in'); img.style.cursor; // => ''Significa que tendremos algunos resultados inesperados en algunas situaciones como que no podemos quitar el cursor:
$('<img style="cursor: zoom-in">', $sandboxDoc) .css({cursor : ''}).prop('outerHTML'); // => '<img style="cursor: zoom-in">' // by pure javascript img.style.cursor = ''; img.outerHTML; // => '<img style="cursor:zoom-in">'Para evitar esto, debemos asegurarnos de construir nodos DOM antes de eliminar el documento:
function $sandbox(content) { const $frame = $('<iframe>').appendTo('body'); const $elem = $(content, $frame.contents()); $frame.remove(); return $elem; } $sandbox('<img style="cursor: zoom-in">').css('cursor'); // => 'zoom-in' // by pure javascript function sandbox(fn) { const frame = document.createElement('iframe'); document.body.appendChild(frame); const elem = fn(frame.contentDocument); frame.parentNode.removeChild(frame); return elem; } sandbox(sandboxDoc => { const img = sandboxDoc.createElement('img'); img.setAttribute('style', 'cursor:zoom-in'); return img; }).style.cursor; // => 'zoom-in'