Ahora que todos los navegadores principales admiten iframes de espacio aislado, hay una forma mucho más sencilla de que creo que puede ser segura. Me encantaría que esta respuesta pudiera ser revisada por personas más familiarizadas con este tipo de problema de seguridad.
NOTA: Este método definitivamente no funcionará en IE 9 y versiones anteriores. Consulte esta tabla para conocer las versiones del navegador que admiten la zona de pruebas. (Nota: la tabla parece indicar que no funcionará en Opera Mini, pero lo intenté y funcionó).
La idea es crear un iframe oculto con JavaScript desactivado, pegar su HTML que no es de confianza en él y dejar que lo analice. Luego, puede recorrer el árbol DOM y copiar las etiquetas y atributos que se consideran seguros.
Las listas blancas que se muestran aquí son solo ejemplos. Lo mejor para incluir en la lista blanca dependería de la aplicación. Si necesita una política más sofisticada que solo listas blancas de etiquetas y atributos, puede acomodarla con este método, aunque no con este código de ejemplo.
var tagWhitelist_ = {
'A': true,
'B': true,
'BODY': true,
'BR': true,
'DIV': true,
'EM': true,
'HR': true,
'I': true,
'IMG': true,
'P': true,
'SPAN': true,
'STRONG': true
};
var attributeWhitelist_ = {
'href': true,
'src': true
};
function sanitizeHtml(input) {
var iframe = document.createElement('iframe');
if (iframe['sandbox'] === undefined) {
alert('Your browser does not support sandboxed iframes. Please upgrade to a modern browser.');
return '';
}
iframe['sandbox'] = 'allow-same-origin';
iframe.style.display = 'none';
document.body.appendChild(iframe);
iframe.contentDocument.body.innerHTML = input;
function makeSanitizedCopy(node) {
if (node.nodeType == Node.TEXT_NODE) {
var newNode = node.cloneNode(true);
} else if (node.nodeType == Node.ELEMENT_NODE && tagWhitelist_[node.tagName]) {
newNode = iframe.contentDocument.createElement(node.tagName);
for (var i = 0; i < node.attributes.length; i++) {
var attr = node.attributes[i];
if (attributeWhitelist_[attr.name]) {
newNode.setAttribute(attr.name, attr.value);
}
}
for (i = 0; i < node.childNodes.length; i++) {
var subCopy = makeSanitizedCopy(node.childNodes[i]);
newNode.appendChild(subCopy, false);
}
} else {
newNode = document.createDocumentFragment();
}
return newNode;
};
var resultElement = makeSanitizedCopy(iframe.contentDocument.body);
document.body.removeChild(iframe);
return resultElement.innerHTML;
};
Puedes probarlo aquí .
Tenga en cuenta que estoy rechazando los atributos y etiquetas de estilo en este ejemplo. Si los permitiste, probablemente querrás analizar el CSS y asegurarte de que sea seguro para tus propósitos.
Probé esto en varios navegadores modernos (Chrome 40, Firefox 36 Beta, IE 11, Chrome para Android) y en uno antiguo (IE 8) para asegurarme de que funcionara antes de ejecutar cualquier script. Me interesaría saber si hay navegadores que tengan problemas con él, o casos extremos que estoy pasando por alto.