¿Qué es XSS? La vulnerabilidad que permite ejecutar código en un navegador
Cross-Site Scripting, más conocido como XSS, es una vulnerabilidad de seguridad web que permite introducir código malicioso en una página legítima. Ese código se ejecuta en el navegador de otros usuarios como si formara parte del sitio original.
Aunque su nombre puede traducirse como “secuencias de comandos entre sitios”, XSS no debe confundirse con CSS, el lenguaje utilizado para diseñar páginas web. La letra X se utiliza como abreviatura de “Cross”.
Esta vulnerabilidad aparece principalmente cuando una aplicación recibe información del usuario y la inserta en una página sin validarla, codificarla o sanearla correctamente.
¿Cómo funciona un ataque XSS?
Una aplicación web recibe información constantemente a través de:
- Formularios.
- Cuadros de búsqueda.
- Comentarios.
- Parámetros de una URL.
- Nombres de usuario.
- Mensajes privados.
- Datos almacenados en una base de datos.
- Fragmentos de la dirección web.
El problema surge cuando la aplicación incorpora esos datos directamente dentro del HTML de la página.
Supongamos que un buscador muestra este mensaje:
Resultados para: guitarraInternamente, la aplicación podría generar el contenido mediante una operación similar a esta:
resultados.innerHTML = "Resultados para: " + busqueda;Si el valor de busqueda contiene etiquetas o código JavaScript, el navegador podría interpretarlo como parte de la página en lugar de mostrarlo como texto.
Un ejemplo clásico utilizado en laboratorios controlados es:
<script>alert("Prueba XSS")</script>Si la aplicación ejecuta el código y muestra una ventana de alerta, significa que existe un problema de seguridad. La alerta no es peligrosa por sí misma: se utiliza como una prueba sencilla para demostrar que el navegador está interpretando una entrada como código.
Estas comprobaciones solo deben realizarse en aplicaciones propias, laboratorios educativos o sistemas para los cuales se tenga autorización expresa.
¿Por qué XSS es peligroso?
Una vulnerabilidad XSS permite ejecutar JavaScript dentro del contexto de un sitio legítimo. Para el navegador, el código inyectado puede parecer parte de la aplicación original.
Dependiendo de las protecciones existentes, un atacante podría intentar:
- Modificar el contenido visible de una página.
- Crear formularios de inicio de sesión falsos.
- Redirigir a los usuarios hacia sitios fraudulentos.
- Realizar acciones utilizando la sesión de la víctima.
- Leer información accesible desde el navegador.
- Capturar datos introducidos en formularios.
- Manipular elementos del DOM.
- Difundir contenido malicioso entre otros usuarios.
- Engañar a la víctima mediante técnicas de phishing.
El impacto depende del funcionamiento de la aplicación, los permisos del usuario afectado y las medidas de seguridad implementadas.
Una vulnerabilidad en una página pública puede tener un efecto limitado. En cambio, si el código se ejecuta en el navegador de un administrador, las consecuencias podrían ser mucho más graves.
Principales tipos de XSS
XSS suele clasificarse en tres categorías principales: reflejado, almacenado y basado en DOM.
1. XSS reflejado
El XSS reflejado ocurre cuando una entrada maliciosa forma parte de una solicitud y la aplicación la devuelve inmediatamente dentro de la respuesta.
Puede aparecer en:
- Buscadores internos.
- Mensajes de error.
- Parámetros de una URL.
- Formularios que muestran los datos enviados.
- Páginas que generan contenido dinámicamente.
Imaginemos una dirección como esta:
https://ejemplo.com/buscar?q=guitarraLa página podría mostrar:
Resultados para guitarraSi el parámetro q se incorpora al documento sin una codificación adecuada, alguien podría construir un enlace especialmente manipulado.
Normalmente, la víctima debe abrir ese enlace para que se produzca la ejecución. Por eso, este tipo de ataque suele combinarse con ingeniería social, correos engañosos o mensajes publicados en redes sociales.
2. XSS almacenado
El XSS almacenado, también llamado persistente, aparece cuando el contenido introducido se guarda en una base de datos, archivo u otro sistema de almacenamiento.
Posteriormente, la aplicación muestra ese contenido a diferentes usuarios y el navegador ejecuta el código.
Puede encontrarse en:
- Secciones de comentarios.
- Foros.
- Perfiles de usuario.
- Sistemas de mensajería.
- Paneles administrativos.
- Campos de descripción.
- Plataformas de soporte técnico.
Este tipo de XSS suele ser especialmente peligroso porque la víctima no necesita abrir un enlace manipulado. Basta con visitar la página donde se encuentra almacenado el contenido malicioso.
Por ejemplo, si una plataforma guarda un comentario sin sanearlo, cada persona que abra la publicación podría verse afectada.
3. XSS basado en DOM
DOM XSS ocurre cuando el problema se encuentra principalmente en el código JavaScript que se ejecuta en el navegador.
El DOM, o Document Object Model, es la representación que utiliza el navegador para organizar y manipular los elementos de una página.
Consideremos este ejemplo inseguro:
const mensaje = location.hash.substring(1);
document.getElementById("resultado").innerHTML = mensaje;El programa obtiene información del fragmento de la URL y la asigna directamente a innerHTML. Si esa información contiene código interpretable, podría modificar el documento.
En este caso, el servidor puede no recibir ni procesar el contenido peligroso. La vulnerabilidad se produce enteramente dentro del navegador debido al manejo inseguro del DOM.
XSS reflejado, almacenado y DOM: diferencias
| Tipo de XSS | ¿Dónde aparece la entrada? | ¿Se almacena? | Interacción habitual |
|---|---|---|---|
| Reflejado | En la respuesta del servidor | No | La víctima abre una solicitud o enlace manipulado |
| Almacenado | En contenido guardado por la aplicación | Sí | La víctima visita la página afectada |
| Basado en DOM | En el código ejecutado por el navegador | No necesariamente | El navegador procesa una fuente insegura |
Estas categorías pueden superponerse. Por ejemplo, una entrada almacenada podría terminar provocando una manipulación insegura del DOM.
¿Qué es una fuente y qué es un sumidero?
Al estudiar DOM XSS aparecen con frecuencia los conceptos de source y sink.
Una fuente es el lugar desde el cual la aplicación obtiene información potencialmente controlada por un usuario.
Algunas fuentes comunes son:
location.href
location.search
location.hash
document.referrer
window.name
localStorageUn sumidero es una función o propiedad capaz de interpretar esa información de una manera peligrosa.
Ejemplos que requieren especial cuidado:
innerHTML
outerHTML
document.write()
eval()
setTimeout()
setInterval()No todas estas funciones producen automáticamente una vulnerabilidad. El riesgo aparece cuando reciben información no confiable sin el tratamiento adecuado.
Diferencia entre validación, codificación y sanitización
Estos tres conceptos suelen confundirse, pero cumplen funciones diferentes.
Validación
La validación comprueba si un dato cumple con el formato esperado.
Por ejemplo, si un campo solicita una edad, debería aceptar solamente números dentro de un rango razonable.
La validación ayuda a rechazar entradas incorrectas, pero no siempre es suficiente para prevenir XSS.
Codificación de salida
La codificación transforma caracteres especiales para que el navegador los muestre como texto y no los interprete como HTML o JavaScript.
Por ejemplo, el símbolo < puede representarse como:
<De esta manera, una etiqueta introducida por el usuario aparece en pantalla, pero no se ejecuta.
La codificación debe aplicarse según el contexto. No se utiliza exactamente la misma técnica para contenido HTML, atributos, JavaScript, CSS o direcciones URL.
Sanitización
La sanitización analiza contenido y elimina o neutraliza elementos considerados peligrosos.
Es necesaria cuando una aplicación debe permitir cierta cantidad de HTML, como podría suceder en un editor de texto enriquecido.
En ese caso, no basta con eliminar la palabra script. Existen numerosas etiquetas, atributos y contextos capaces de producir comportamientos inesperados. Lo recomendable es utilizar una biblioteca reconocida y mantenerla actualizada.
Un ejemplo de código inseguro y su corrección
El siguiente código inserta directamente una entrada dentro del documento:
const nombre = obtenerNombre();
document.getElementById("saludo").innerHTML = "Hola, " + nombre;Si no necesitamos interpretar HTML, una alternativa más segura es utilizar textContent:
const nombre = obtenerNombre();
document.getElementById("saludo").textContent = "Hola, " + nombre;textContent trata el valor como texto. Por lo tanto, las etiquetas no se interpretan como elementos HTML.
Otra opción segura consiste en crear los elementos explícitamente:
const saludo = document.createElement("p");
saludo.textContent = "Hola, " + nombre;
document.body.appendChild(saludo);La idea fundamental es evitar que datos no confiables se conviertan accidentalmente en código ejecutable.
Cómo prevenir vulnerabilidades XSS
No existe una única medida capaz de resolver todos los casos. La prevención requiere varias capas de seguridad.
1. Codificar los datos al mostrarlos
Toda información no confiable debe codificarse antes de incorporarse a una página.
Muchos motores de plantillas y frameworks modernos realizan escape automático. Sin embargo, esta protección puede perderse cuando el desarrollador utiliza funciones que insertan HTML sin procesar.
2. Evitar la inserción directa de HTML
Siempre que sea posible, conviene utilizar propiedades como:
textContenten lugar de:
innerHTMLSi una aplicación necesita aceptar HTML, debe sanearlo mediante una solución especializada.
3. Utilizar frameworks correctamente
React, Angular, Vue y otros frameworks incluyen mecanismos para reducir la posibilidad de XSS. No obstante, pueden volverse inseguros si se desactivan sus protecciones o se utilizan funciones diseñadas para insertar HTML directamente.
Un framework ayuda, pero no reemplaza las buenas prácticas.
4. Implementar Content Security Policy
Content Security Policy, o CSP, es una política enviada por el servidor que limita los recursos y scripts que el navegador puede ejecutar.
Una política restrictiva puede reducir el impacto de determinadas inyecciones. Sin embargo, CSP debe considerarse una defensa adicional y no un sustituto de la codificación y sanitización correctas.
5. Proteger las cookies
Las cookies de sesión deberían utilizar atributos adecuados, como:
HttpOnly.Secure.SameSite.
HttpOnly impide que JavaScript acceda directamente a una cookie. Esto dificulta determinados ataques, aunque no elimina la vulnerabilidad XSS ni impide necesariamente que se realicen acciones dentro de una sesión activa.
6. Validar los datos recibidos
La aplicación debe definir qué formato espera y rechazar entradas que no lo cumplan.
Es preferible utilizar listas de valores permitidos cuando el contexto lo haga posible, en lugar de intentar detectar una lista interminable de contenidos peligrosos.
7. Realizar pruebas de seguridad
Las aplicaciones deberían revisarse mediante:
- Análisis estático del código.
- Pruebas dinámicas.
- Revisiones manuales.
- Escáneres de seguridad.
- Pruebas unitarias.
- Auditorías de dependencias.
- Programas de divulgación responsable.
Las pruebas deben realizarse con autorización y procurando no afectar datos reales ni servicios en producción.
Errores frecuentes al intentar prevenir XSS
Uno de los errores más habituales consiste en bloquear solamente la etiqueta <script>. JavaScript puede activarse a través de diferentes elementos, atributos y contextos, por lo que una lista manual suele ser incompleta.
Otros errores comunes son:
- Sanear los datos solamente en el navegador.
- Confiar en que un firewall bloqueará todas las variantes.
- Aplicar una misma codificación en cualquier contexto.
- Desactivar el escape automático del framework.
- Utilizar expresiones regulares como única defensa.
- Creer que
HttpOnlyelimina completamente el riesgo. - Mantener bibliotecas de sanitización sin actualizar.
- Confundir validación de entrada con codificación de salida.
La defensa debe diseñarse de acuerdo con el lugar exacto donde se insertará la información.
¿Cómo se detecta XSS de manera ética?
Durante una auditoría autorizada, el profesional identifica los puntos donde la aplicación recibe y muestra datos.
El proceso general consiste en:
- Localizar entradas controlables.
- Observar dónde aparecen sus valores.
- Determinar el contexto de salida.
- Comprobar si los caracteres especiales son codificados.
- Revisar el código JavaScript relacionado.
- Utilizar una prueba inocua y claramente identificable.
- Documentar el resultado.
- Informar el problema al responsable del sistema.
No es necesario intentar acceder a datos privados para demostrar la vulnerabilidad. Una prueba mínima, como la modificación controlada de un elemento o una alerta dentro de un laboratorio, suele ser suficiente.
Laboratorios seguros para aprender
Quienes deseen estudiar XSS deberían utilizar entornos creados específicamente para practicar, como:
- Aplicaciones vulnerables instaladas localmente.
- Máquinas virtuales de laboratorio.
- Plataformas educativas de ciberseguridad.
- Entornos de captura de bandera o CTF.
- Proyectos propios preparados para realizar pruebas.
Nunca se debe probar una carga XSS en una página ajena sin autorización. Incluso una prueba aparentemente inofensiva puede violar las condiciones del servicio, afectar usuarios o generar consecuencias legales.
¿XSS sigue siendo relevante?
Sí. Las aplicaciones modernas utilizan una enorme cantidad de JavaScript y generan contenido dinámicamente. Aunque los frameworks actuales incluyen protecciones importantes, los errores de implementación continúan produciendo vulnerabilidades.
El riesgo aumenta cuando una aplicación:
- Combina datos de diferentes fuentes.
- Inserta HTML dinámicamente.
- Utiliza componentes antiguos.
- Permite contenido enriquecido.
- Confía excesivamente en filtros personalizados.
- Desactiva mecanismos de seguridad del framework.
- Incorpora bibliotecas de terceros sin revisarlas.
Por eso, XSS continúa siendo un tema esencial tanto para desarrolladores web como para profesionales de ciberseguridad.
Conclusión
Cross-Site Scripting es una vulnerabilidad que transforma información controlada por un usuario en contenido ejecutable dentro del navegador.
Sus tres formas principales son XSS reflejado, almacenado y basado en DOM. Aunque cada una funciona de manera diferente, comparten una causa general: la aplicación trata datos no confiables como si fueran código seguro.
La prevención requiere codificación contextual, sanitización cuando sea necesaria, uso cuidadoso del DOM, cookies protegidas, una política CSP adecuada y revisiones periódicas de seguridad.
Comprender XSS no solo ayuda a encontrar vulnerabilidades. También permite desarrollar aplicaciones más resistentes y tomar mejores decisiones al trabajar con datos introducidos por los usuarios.
Preguntas frecuentes
¿Qué significa XSS?
XSS significa Cross-Site Scripting. La letra X representa la palabra “Cross” y permite diferenciar esta vulnerabilidad de CSS.
¿XSS afecta al servidor o al navegador?
El código inyectado se ejecuta principalmente en el navegador de la víctima. Sin embargo, la vulnerabilidad puede originarse en el servidor, en el código del cliente o en una combinación de ambos.
¿XSS puede robar contraseñas?
Una explotación podría intentar capturar información introducida en formularios o presentar interfaces falsas. El resultado dependerá de las protecciones de la aplicación y del navegador.
¿HttpOnly evita XSS?
No. HttpOnly protege una cookie contra el acceso directo desde JavaScript, pero no corrige la vulnerabilidad ni impide todas las acciones posibles dentro de una sesión.
¿Usar un framework moderno elimina el problema?
No completamente. Los frameworks reducen el riesgo mediante escape automático y otras protecciones, pero una implementación insegura todavía puede introducir XSS.
¿Es legal probar XSS en cualquier página?
No. Las pruebas deben limitarse a sistemas propios, laboratorios educativos o aplicaciones para las cuales exista una autorización expresa.
Descripción para buscadores: Descubre qué es XSS, cómo funcionan sus variantes reflejada, almacenada y DOM, cuáles son sus riesgos y cómo prevenir esta vulnerabilidad web.
Comentarios
Publicar un comentario