Glosario
Qué es el clickjacking: trampas invisibles que te hacen pulsar botones ocultos
El clickjacking superpone de forma invisible una página real sobre el sitio del atacante para que un usuario con sesión iniciada pulse botones 'ocultos'. Cómo funciona y la defensa real (CSP frame-ancestors y X-Frame-Options para que tu sitio no pueda enmarcarse), explicado en clave defensiva, sin pasos de ataque.
"El botón que pulsaste a propósito era en realidad el botón de una página oculta por debajo": eso es el clickjacking. Aquí tienes cómo funciona y cómo prevenirlo de forma fiable (sin pasos de ataque).
Qué se ataca
Cualquier cosa que sea "un clic, listo, y valioso".
| Objetivo probable | Por qué |
|---|---|
| Confirmar una transferencia/compra | Un clic mueve dinero |
| Cambio de configuración (visibilidad, apps conectadas) | Prepara un secuestro posterior o el acceso a datos |
| "Permitir" de OAuth / permisos | Aprueba en silencio la vinculación de una cuenta |
| Seguir / me gusta / publicar en redes | Se abusa para difusión y granjas de "me gusta" |
Por qué funciona
Los navegadores pueden cargar otro sitio en un iframe y apilarlo encima. El atacante hace ese iframe transparente (opacity 0), de modo que el usuario solo ve el señuelo de la interfaz colocado debajo. Cuando el usuario pulsa el "botón falso", el botón real superpuesto justo encima recibe el clic.
Se parece a CSRF, pero CSRF envía una solicitud entre bambalinas, mientras que el clickjacking hace que el usuario opere la pantalla real: por eso los tokens CSRF por sí solos no lo detienen.
Defensa: el arreglo real es "no poder ser enmarcado"
Configura CSP frame-ancestors (lo más importante)
Añade Content-Security-Policy: frame-ancestors 'self' (solo tu propio origen puede enmarcarte). Si nadie necesita incrustarte, 'none' es lo más seguro. Lista dominios solo para los socios que permitas explícitamente.
Añade también X-Frame-Options (retrocompatible)
Para clientes antiguos, envía también X-Frame-Options: DENY (o SAMEORIGIN). Cinturón y tirantes para navegadores viejos y nuevos.
Exige un segundo paso en las acciones críticas
Protege las transferencias y las concesiones de permisos tras una reautenticación, un diálogo de confirmación o una comprobación de intención: más difícil de forzar mediante una superposición silenciosa.
Marca las cookies de sesión como SameSite
SameSite=Lax/Strict frena el envío automático entre sitios. No es un arreglo completo del clickjacking por sí solo, pero debilita la clase más amplia de "acción iniciada desde otro sitio".
La opinión de este sitio: una línea de encabezado hace lo más importante — mide primero tu propio sitio
La defensa frente al clickjacking no es código sofisticado; son una o dos líneas de encabezado de respuesta que eliminan la base que necesita una superposición. Aplícalas en todo el sitio desde el framework o el proxy inverso (Caddy/nginx) para que nada se cuele. Puedes comprobar si frame-ancestors está realmente activo con el verificador de encabezados de seguridad en las herramientas de este sitio. "Creía que lo había puesto" —el encabezado que falta— es el fallo más común.
Sigue leyendo
- Glosario: Qué es CSRF · Qué es XSS
- Comprueba: Herramientas de seguridad gratuitas (verificador de encabezados de seguridad)
FAQ
Q¿Qué puede hacer el clickjacking?
Un clic que el usuario hace 'a propósito' cae sobre el botón de una página real superpuesto por debajo. Se abusan acciones críticas de un solo clic: transferencias de dinero, cambios de configuración, concesión de permisos, seguir/dar me gusta en redes, botones de consentimiento. No roba una contraseña: hace que el usuario, ya con sesión iniciada, realice la acción.
Q¿Cuál es la principal defensa?
Negarse a ser incrustado en el frame de otro sitio. Configura la directiva CSP frame-ancestors a 'self' (o solo a los dominios permitidos) en los encabezados de respuesta, y añade X-Frame-Options: DENY por retrocompatibilidad. Eso elimina la superficie que necesita una superposición.
Q¿Basta con el frame-busting en JavaScript?
No. Los viejos scripts de 'salir si estoy en un iframe' tienen muchos bypasses y no ayudan cuando JS está deshabilitado. El arreglo real son los encabezados del servidor (frame-ancestors / X-Frame-Options) que indican al navegador que rechace la incrustación.