El DOM-based XSS es una vulnerabilidad de cross-site scripting que vive por completo en el código client-side. El propio JavaScript de la página lee entradas controlables por el atacante — la mayoría de las veces desde la URL — y las escribe en el DOM a través de un sink inseguro como innerHTML, ejecutando el script del atacante. A diferencia del XSS reflejado o almacenado, el payload malicioso puede no aparecer nunca en ninguna respuesta del servidor.
¿Cómo funciona un ataque de DOM XSS?
La vulnerabilidad es un flujo de datos desde un source que el atacante controla hasta un sink que interpreta texto como código o marcado:
// vulnerable: fragment flows straight into a sink
const name = decodeURIComponent(location.hash.slice(1));
document.querySelector("#welcome").innerHTML = "Hello " + name;
// https://example.com/#<img src=x onerror=stealCookies()>
Sources comunes: location.hash, location.search, document.referrer, window.name, los datos de postMessage y el almacenamiento client-side. Sinks comunes: innerHTML/outerHTML, document.write, eval y Function, el .html() de jQuery, y escrituras de atributos que crean manejadores o URLs (onclick, href con javascript:).
Como el fragmento de la URL nunca se envía al servidor, un payload después de # puede explotar la página mientras el servidor ve una petición perfectamente normal.
XSS reflejado vs almacenado vs DOM-based
| XSS reflejado | XSS almacenado | DOM-based XSS | |
|---|---|---|---|
| Dónde vive el payload | En la petición, devuelto por el servidor | En la base de datos del servidor | En la entrada client-side (a menudo el fragmento de la URL) |
| ¿La respuesta del servidor contiene el payload? | ✓ | ✓ | A menudo ✗ |
| ¿Visible para WAF / logs del servidor? | Normalmente | Normalmente | A menudo no |
| Dónde se corrige | Codificación de salida en el servidor | Sanitización + codificación en el servidor | Código client-side: sinks seguros, sanitización |
Por qué el DOM XSS es fácil de pasar por alto
Las defensas del lado del servidor inspeccionan peticiones y respuestas; el DOM XSS puede eludir ambas. Los frameworks reducen el riesgo — React y similares escapan por defecto — pero dangerouslySetInnerHTML, el mal uso de plantillas y los scripts de terceros en la página lo reintroducen. Y una página moderna ejecuta docenas de scripts que tú no escribiste: un sink inseguro en cualquiera de ellos es un sink inseguro en tu página. Ese problema de composición es el núcleo de la seguridad client-side.
Cómo prevenirlo y detectarlo
- Prefiere sinks seguros:
textContenten lugar deinnerHTML; evita por completoevaly los manejadores construidos con strings. - Sanitiza cuando el HTML sea inevitable (DOMPurify o la emergente Sanitizer API), y adopta Trusted Types donde estén soportados — convierten las escrituras en sinks inseguros en violaciones de política.
- Despliega una Content Security Policy estricta con nonces; bloquea muchos estilos de payload, aunque no toda la clase de ataque.
- Observa el comportamiento en tiempo de ejecución: la explotación acaba manifestándose como scripts que hacen cosas que no deberían — nuevas peticiones salientes, escrituras en el DOM sobre formularios de pago, listeners inesperados. La monitorización client-side de cside observa sesiones reales en la capa del navegador, que es el único lugar donde un ataque exclusivo del DOM es visible siquiera.








