Inyecciones CSS (CSSI): cuando un simple color abre la puerta al XSS 🎨
Las Inyecciones CSS (CSSI) son un tipo de vulnerabilidad web que permite a un atacante inyectar código CSS malicioso en una página, aprovechando que la aplicación toma una entrada del usuario y la vuelca directamente en su hoja de estilos sin validarla ni sanearla. A simple vista puede parecer un fallo inofensivo («total, solo es CSS, no puede ejecutar código»), pero como vamos a ver, puede escalar hasta convertirse en un vector de Cross-Site Scripting (XSS) en toda regla.
🔍 Escenario
Para practicar esto usamos el laboratorio skf-labs/nodejs/CSSI. Lo primero, instalamos las dependencias del proyecto:
npm install
El laboratorio tiene un formulario que permite introducir un color. Por ejemplo, si escribimos red, la aplicación genera dinámicamente la siguiente etiqueta de estilo:
<style>
p.colorful {
color: red
}
</style>
El problema es evidente en cuanto nos fijamos en cómo se construye ese bloque: el valor que introducimos se inserta tal cual dentro del CSS, sin ningún tipo de filtrado.
💡 Paso 1 — Inyectar una propiedad CSS extra
Si en el campo del formulario metemos:
red;background-color:green
Estamos inyectando CSS adicional dentro de la misma regla, alterando el estilo del elemento más allá de lo que el desarrollador esperaba. Es un primer indicio de que no hay ningún control sobre la entrada, pero el impacto real va mucho más allá de este ejemplo.
💡 Paso 2 — Escapar de la regla CSS
Si cerramos la llave de la regla actual, podemos definir estilos que afecten a cualquier otro selector de la página, por ejemplo body:
red}body{ background-color:yellow
Aquí ya no estamos modificando solo el elemento original: estamos escapando del contexto previsto por el desarrollador y tomando el control de la hoja de estilos completa.
💡 Paso 3 — De CSS a JavaScript
El golpe final llega cuando, además de cerrar la regla CSS, cerramos también la propia etiqueta <style> y aprovechamos para inyectar una etiqueta <script>:
red}</style><script>alert("HACKED")</script>
En este punto ya no hablamos de una simple inyección de estilos: hemos conseguido ejecutar código JavaScript arbitrario en el contexto de la página, es decir, un XSS clásico camuflado detrás de un campo que en teoría solo aceptaba un color.
🚫 ¿Qué salió mal?
El fallo de fondo es siempre el mismo: la aplicación confía en una entrada no confiable del usuario y la incrusta directamente en el código CSS (o HTML) de la página, sin validar ni escapar los caracteres especiales como ;, } o <. Esto convierte un campo aparentemente inocuo en una puerta de entrada para manipular el diseño de la página o, en el peor de los casos, para ejecutar JavaScript.
Las Inyecciones CSS pueden usarse por sí solas como vector de ataque para explotar vulnerabilidades de Cross-Site Scripting (XSS). Imagina una aplicación web que permite a los usuarios introducir texto en un campo que luego se muestra en la página: si el desarrollador no valida ni filtra correctamente esa entrada, un atacante podría inyectar código malicioso, incluyendo JavaScript, tal como hemos visto en el paso 3.
Además, si el código CSS inyectado es lo «suficientemente complejo», puede llegar a hacer que el navegador interprete el código de una forma que termine desencadenando la ejecución de JavaScript, lo que se conoce como inyección de JavaScript inducida por CSS (CSS-Induced JavaScript Injection). Una vez que el atacante logra ese punto de entrada, puede desplegar un ataque XSS completo: robar credenciales, secuestrar sesiones o redirigir a la víctima a una página falsa, entre otros muchos escenarios. Para profundizar en técnicas de explotación de CSSI, HackTricks mantiene una recopilación detallada en su página sobre CSS Injection.
✅ Buenas prácticas
- Nunca insertar entradas de usuario directamente en bloques
<style>o atributosstylesin sanear. - Validar los valores esperados contra una lista blanca (por ejemplo, si se espera un color, comprobar que coincide con un patrón de color válido).
- Escapar correctamente los caracteres especiales de CSS/HTML (
;,{,},<,>) antes de insertarlos en el DOM. - Aplicar una política de Content Security Policy (CSP) que restrinja la ejecución de scripts inline.
🔐 Conclusión
Las Inyecciones CSS demuestran que ningún punto de entrada de usuario es «demasiado pequeño» para ser peligroso. Un simple campo de color, mal validado, puede terminar derivando en la ejecución de JavaScript arbitrario y en un ataque de Cross-Site Scripting completo. La lección de siempre: toda entrada de usuario es hostil hasta que se demuestre lo contrario.
