Cómo funciona la web antes de hablar de vulnerabilidades
Las tres capas de la web
Toda página web se compone de tres tecnologías con responsabilidades separadas. Entender esta separación es esencial para comprender dónde y cómo ocurren los ataques XSS.
HTML
La estructura
Define el contenido y su jerarquía. Es el esqueleto del documento: qué elementos existen, en qué orden, cómo se anidan.
JS tiene acceso total al DOM y puede leer document.cookie, hacer fetch a cualquier URL, redirigir al usuario...
¿Por qué importa esto para XSS?
XSS = JavaScript inyectado en una página legítima
Un ataque XSS inyecta código JavaScript en una página legítima. Una vez ejecutado, ese código tiene los mismos permisos que cualquier otro JS de la página: puede leer cookies, modificar el DOM, exfiltrar datos hacia servidores externos, capturar pulsaciones de teclado, o redirigir al usuario a páginas falsas.
Analogía: construir una casa
Una forma de recordar las tres capas y sus roles:
HTML
Paredes, suelo, techo
La estructura física. Define qué existe y dónde está. Sin ella no hay casa.
CSS
Pintura, decoración, muebles
El aspecto visual. Hace la casa habitable y agradable, pero no cambia su estructura.
JavaScript
Electricidad, fontanería, automatización
Lo que hace cosas. Puede encender luces, abrir puertas, enviar señales al exterior.
Separación de responsabilidades
La práctica estándar es mantener cada tecnología en su propio archivo. El navegador carga primero el HTML, y luego descarga los recursos referenciados:
El navegador primero carga el HTML, luego descarga CSS y JS referenciados. Este orden tiene implicaciones de seguridad: un script en el <head> bloquea la renderización; un script al final del <body> se ejecuta cuando el DOM ya está construido. Los ataques XSS pueden inyectar <script> en cualquier punto del HTML o aprovecharse del JS ya existente.
El viaje de una petición web
Entender el ciclo completo de carga de una página es clave para comprender por qué XSS basado en DOM puede ser invisible en las respuestas del servidor.
1
Resolución DNS
El navegador pregunta a un servidor DNS: ¿Cuál es la IP de 'ejemplo.com'? Recibe una respuesta con la dirección IP. Esta conversación es invisible para el usuario.
ejemplo.com → DNS → 93.184.216.34
2
Conexión TCP/TLS
El navegador establece una conexión TCP con el servidor en el puerto 443 (HTTPS). Se negocia TLS para cifrar el canal. Todo lo que se transmita a partir de aquí viaja cifrado.
3
Petición HTTP GET
El navegador envía la petición al servidor. Incluye la URL solicitada, cabeceras del navegador y las cookies del dominio.
HTTP Request
GET /buscar?q=laptop HTTP/1.1
Host: ejemplo.com
Cookie: session=abc123
4
Respuesta: el HTML inicial
El servidor procesa la petición y devuelve HTML. En XSS reflejado, el payload aparece aquí directamente en la respuesta.
HTTP Response — XSS Reflejado
<p>Resultados para: <script>alert(1)</script></p>
<!-- VISIBLE en la respuesta HTTP -->
XSS Reflejado
El payload está en la respuesta del servidor. Se puede ver en el código fuente de la página con Ctrl+U.
5
El navegador analiza el HTML
El parser HTML construye el DOM inicial. Encuentra referencias a recursos externos (CSS, JS, imágenes) y los descarga en paralelo. El árbol DOM empieza a existir en memoria.
6
CSS aplicado al DOM
Los estilos se calculan y aplican a cada nodo del DOM. El árbol de renderizado se construye. En este punto el usuario ve la página "pintada" con su apariencia visual.
7
JavaScript modifica el DOM
Los scripts se ejecutan. Aquí ocurren las peticiones AJAX/fetch para cargar datos dinámicos. El contenido puede cambiar radicalmente respecto al HTML inicial recibido del servidor.
JavaScript — SINK vulnerable
// JS lee la URL y escribe en el DOM
const q = new URLSearchParams(location.search).get('q');
document.getElementById('resultado').innerHTML = q; // SINK vulnerable
XSS basado en DOM
El payload NUNCA llega al servidor. El servidor devuelve HTML "limpio". El navegador ejecuta JS que lee la URL y escribe en el DOM sin sanitizar. No hay rastro en los logs del servidor.
¿Qué ve el servidor vs qué ve el usuario?
Lo que responde el servidor
HTML estático / plantilla renderizada
Datos de la base de datos
Lo que devuelve Ctrl+U (View Source)
XSS Reflejado: VISIBLE aquí
Lo que muestra el navegador
HTML inicial + modificaciones de JS
Contenido cargado vía AJAX/fetch
El resultado final de todas las mutaciones del DOM
XSS en DOM: SOLO visible aquí (F12 → Elements)
Implicación práctica de seguridad
Los escáneres HTTP no ven el DOM XSS
Un escáner que solo analiza la respuesta HTTP puede reportar un sitio como "seguro" aunque tenga un DOM XSS. El payload vive en la URL (#fragment) o se inserta vía JS después de la carga. Solo un navegador headless que ejecute JS puede detectarlo.