¿Qué es XSS (cross-site scripting)? 3 tipos y cómo evitarlo

Imagina un cuadro de comentarios en un blog. En lugar de escribir "buen artículo", alguien escribe un fragmento de JavaScript. Si el sitio guarda ese texto y lo entrega a cada visitante sin tratarlo, el navegador de todos los que abran la página ejecutará el código, creyendo que forma parte del sitio. Eso es XSS (cross-site scripting), una de las fallas web más antiguas y más repetidas, y la que sostiene buena parte del robo de cookies de sesión.
¿Qué es XSS, exactamente?
¿Qué es cross-site scripting (XSS)?
XSS es una vulnerabilidad en la que un sitio incluye en la página texto controlado por el atacante sin tratarlo, y el navegador de la víctima lo ejecuta como JavaScript. El código corre con los mismos permisos del sitio legítimo.
Según la documentación de MDN, como el código se ejecuta en el origen del sitio, puede acceder al contenido de la página, al almacenamiento local y a las credenciales, y hacer solicitudes en nombre del usuario. El ataque exige dos condiciones: el sitio acepta una entrada que el atacante puede construir y la coloca en la página sin tratamiento.
Fíjate en lo que es el problema: no se vulnera el servidor. El blanco es el navegador del visitante, que confía en el sitio y, por extensión, en lo que el sitio entrega.
Los 3 tipos de XSS
XSS reflejado
El servidor toma un parámetro de la URL y lo devuelve en la respuesta sin codificarlo. Ejemplo clásico: una página de búsqueda que muestra "Buscaste: término". El atacante arma un enlace con el código en lugar del término y convence a la víctima de hacer clic, por correo, mensaje o anuncio. Es el mismo guion del phishing por correo, solo que el enlace apunta a un sitio legítimo (y vulnerable).
XSS almacenado (persistente)
El código queda guardado en la base de datos (en un comentario, perfil o mensaje de soporte) y se sirve a todos los que abran la página. MDN lo describe como particularmente grave, porque no depende de que la víctima haga clic en un enlace malicioso: basta con visitar la página. Los paneles administrativos que muestran mensajes de usuarios son un blanco frecuente, ya que el "visitante" ahí es alguien con mucho privilegio.
XSS basado en DOM
Aquí el servidor ni siquiera participa: el propio JavaScript de la página toma un dato (de la URL, por ejemplo) y lo vuelca en el DOM de forma insegura. El patrón típico es usar innerHTML con texto del usuario:
// vulnerable: lo que venga en "usuario" se vuelve HTML
bemvindo.innerHTML = `Olá, ${usuario}!`;
// seguro: lo que venga en “usuario” se vuelve texto plano
bemvindo.textContent = Olá, ${usuario}!;
Si usuario contiene una etiqueta <img> con un manejador de eventos, la primera versión ejecuta el código. La segunda muestra el texto literalmente en pantalla.
¿Qué puede hacer un atacante con XSS?
- Robar la sesión: leer cookies sin la bandera
HttpOnlyy entrar en la cuenta de la víctima sin conocer la contraseña. - Actuar como el usuario: incluso con la cookie protegida, el script puede enviar solicitudes autenticadas: cambiar el correo de la cuenta, hacer una transferencia, publicar contenido.
- Mostrar formularios falsos dentro del sitio verdadero para capturar contraseñas o datos de tarjeta.
- Redirigir a la víctima a páginas de estafa o descargas de malware.
Por eso el 2FA y una contraseña fuerte tampoco lo resuelven por sí solos: el código malicioso actúa después del inicio de sesión, dentro de la sesión ya autenticada.
Cómo evitar XSS (si desarrollas o mantienes un sitio)
La defensa principal es tratar todo dato no confiable en el punto donde se escribe en la página. La Cheat Sheet de prevención de XSS de OWASP lo resume bien: lo que importa es la cobertura; una aplicación que protege 99 lugares y olvida uno sigue siendo vulnerable.
- Codificar la salida según el contexto. Texto en HTML, valores de atributo, parámetros de URL y cadenas dentro de scripts piden codificaciones distintas. Los caracteres
<,>,",'y&pasan a entidades HTML en el cuerpo de la página. - Usar el escape automático del framework y desconfiar de las salidas de emergencia:
dangerouslySetInnerHTMLen React,bypassSecurityTrust*en Angular,unsafeHTMLen Lit. El nombre ya avisa. - Preferir
textContentainnerHTML. Si de verdad se necesita HTML del usuario (editor de texto enriquecido), pásalo por un sanitizador mantenido, como DOMPurify, que OWASP recomienda, y no modifiques el contenido después. - Poner comillas en los atributos. Un atributo sin comillas deja que el atacante "cierre" el valor y agregue un evento propio.
- Nunca insertar entrada del usuario dentro de
<script>o<style>: MDN lo considera casi siempre inseguro.
¿CSP y HttpOnly resuelven XSS?
No. Content Security Policy (CSP) y la cookie HttpOnly reducen el daño, pero no corrigen la falla. OWASP dice explícitamente que la CSP no debe ser la defensa principal.
Una CSP estricta, con nonce o hash, hace que el navegador rechace scripts inyectados y bloquea manejadores de eventos en línea y eval(). La bandera HttpOnly impide que JavaScript lea la cookie de sesión. Ambas sirven como capa extra, pero un script inyectado aún puede actuar dentro de la sesión. Por último, Trusted Types (require-trusted-types-for 'script') obliga a los puntos peligrosos del DOM a recibir valores pasados por un sanitizador; no sanitiza por sí solo, solo garantiza que se llamó a tu función, y el soporte entre navegadores aún es desigual.
¿Y si solo soy usuario?
No puedes corregir el código del sitio, pero sí reducir el riesgo:
- Desconfía de enlaces de búsqueda o de inicio de sesión con una URL muy larga y llena de símbolos, recibidos en un mensaje inesperado.
- Cierra sesión en servicios sensibles (banco, panel administrativo) al terminar, para acortar la ventana de una sesión robada.
- Mantén el navegador actualizado y usa pocas extensiones: una extensión maliciosa también ejecuta código dentro de tus páginas.
- Si un sitio se comporta de forma extraña (una ventana de inicio de sesión de la nada, una redirección), cierra la pestaña y avisa al responsable.
La lección de fondo
XSS y SQL Injection comparten la misma raíz: datos y comandos mezclados en el mismo texto. OWASP, de hecho, agrupa el XSS en la categoría Injection del Top 10 de 2021. Cambió el blanco (la base de datos en SQLi, el navegador en XSS), no la causa. Si quieres ver la misma idea aplicada a los chatbots, lee Prompt Injection: qué es y por qué amenaza a los chatbots de IA.
Fuentes consultadas: MDN Web Docs (Cross-site scripting), OWASP Cross Site Scripting Prevention Cheat Sheet.




Comentarios