FlawOpen

flawopen.com/XSS in JavaScript/DOMPurify and innerHTML

¿DOMPurify basta para que innerHTML sea seguro?

Guía de referencia
Respuesta breve

DOMPurify es un sanitizador de lista de permitidos sólido para HTML enriquecido si su salida llega directamente a un sink HTML sin modificarse después. Es como revisar un paquete en el muelle: si otro proceso lo vuelve a empaquetar o lo introduce por otra entrada, la inspección inicial ya no cubre lo que llega. Para texto normal, usa textContent y evita interpretar HTML.

SIGUE SIENDO RIESGOSO — salida antigua llega al sink
// Sanitizado al guardar con una versión/política antigua.
saveToDb(DOMPurify.sanitize(input));

// Después se confía en el texto guardado sin revisarlo.
el.innerHTML = fetchedFromDb;
MÁS SEGURO — sanitiza para este sink HTML
// Para HTML enriquecido, sanitiza con la versión actual
// justo antes de insertarlo en contexto HTML.
el.innerHTML = DOMPurify.sanitize(untrustedHtml, {
  USE_PROFILES: { html: true },
});

// Para texto normal, no lo interpretes como HTML.
textNode.textContent = untrustedText;

Por qué sanitizar una vez al guardar es un patrón más débil

Sanitizar al guardar no es inseguro por sí solo, pero crea una frontera de datos obsoletos: el renderizador podría confiar en una salida antigua tras una corrección del sanitizador, un cambio de política o una ruta que omitió el paso. Vuelve a sanitizar al renderizar con la versión actual y la política del sink concreto. No modifiques ni pases el marcado limpio por otra biblioteca antes de insertarlo; el postprocesamiento puede anular la sanitización.

La configuración y el contexto de salida importan

La configuración predeterminada de DOMPurify permite HTML, SVG y MathML. Si solo necesitas HTML, limita el perfil con USE_PROFILES: { html: true }. No amplíes etiquetas o atributos sin revisión. DOMPurify no sanitiza CSS ni impide todas las solicitudes externas; usa el resultado solo en contexto HTML, nunca en JavaScript, atributos, XML, SVG o CSS.

Trusted Types y renderizado en servidor

Cuando esté disponible, exige Trusted Types mediante Content Security Policy para rechazar cadenas en sinks HTML salvo que pasen por una política aprobada. DOMPurify puede producir TrustedHTML; esto reduce usos accidentales del sink, pero no corrige una configuración insegura ni el postprocesamiento. En servidor, DOMPurify depende de una implementación DOM: mantén jsdom actualizado y no lo combines con happy-dom para sanitización sensible a la seguridad.

Lista de verificación de producción

Busca sinks de inserción HTML

rg -n -g '*.{js,ts,jsx,tsx}' '(innerHTML|outerHTML|insertAdjacentHTML|document\.write|dangerouslySetInnerHTML)' .
En cada coincidencia, sigue los datos no confiables hasta el sink, confirma que la sanitización ocurre justo antes de insertarlos y comprueba que ningún código posterior modifica el marcado.

Fuentes y referencias

Véase también Cross-Site Scripting (XSS, CWE-79): cómo funciona y cómo evitarlo en cada lenguaje — flawopen.com ¿Markdown es seguro frente a XSS? ¿Svelte protege contra XSS por defecto? ¿La carga de SVG implica riesgo de XSS? Riesgo de XSS con Vue v-html WKWebView: ¿HTML no confiable seguro? XSS reflejado, almacenado y basado en DOM: ¿en qué se diferencian?