Command Palette

Search for a command to run...

Prevención de Clickjacking: Protege tu Aplicación contra Ataques de Interfaz

Aprende qué es el clickjacking, cómo funciona, y las técnicas efectivas para prevenirlo usando X-Frame-Options, CSP frame-ancestors y JavaScript frame-busting.

Lectura: 12 min
Nivel: Intermedio

TL;DR - Resumen rápido

  • El clickjacking es un ataque que engaña al usuario para hacer clic en elementos invisibles o disfrazados
  • X-Frame-Options es un header HTTP que controla quién puede incrustar tu sitio en iframes
  • CSP frame-ancestors es la versión moderna y más flexible de X-Frame-Options
  • Las técnicas de frame-busting en JavaScript pueden detectar si tu sitio está en un iframe
  • Combina múltiples capas de protección para una defensa robusta contra clickjacking

Introducción al Clickjacking

El clickjacking es una técnica de ataque que usa un <iframe> invisible con opacity: 0 y position: absolute para superponer tu sitio sobre elementos engañosos. El atacante posiciona el iframe transparente exactamente sobre un botón como "¡Gana un iPhone!", pero el usuario en realidad está haciendo clic en botones de tu aplicación como "Transferir dinero" o "Eliminar cuenta".

Este ataque es peligroso porque no requiere credenciales ni descargas de malware. Solo necesita que el usuario haga clic en un elemento aparentemente inofensivo. Los atacantes pueden usar clickjacking para robar cookies, realizar transacciones no autorizadas, cambiar configuraciones de cuenta, o propagar malware a través de redes sociales.

Impacto del Clickjacking

El clickjacking puede comprometer la seguridad incluso con otras medidas de protección implementadas. Un usuario autenticado puede ser engañado para realizar acciones críticas como transferir fondos, cambiar contraseñas o eliminar datos, todo sin darse cuenta de que está interactuando con tu aplicación.

Cómo Funciona el Ataque

El atacante crea una página maliciosa que contiene un iframe apuntando a tu sitio web. Este iframe se configura con dimensiones específicas y se posiciona estratégicamente usando CSS para superponerse sobre elementos visuales engañosos. El atacante puede hacer el iframe completamente transparente o solo transparente en ciertas áreas, haciendo que los elementos de tu sitio sean invisibles pero aún interactivos.

Por ejemplo, un atacante puede superponer un botón de "Ganar un iPhone" sobre un botón de "Transferir dinero" en tu aplicación bancaria. Cuando el usuario hace clic en el botón promocional, en realidad está ejecutando la transferencia de dinero. El usuario cree que está participando en una promoción legítima, mientras que su cuenta está siendo vaciada silenciosamente.

  • <strong>Ataques de robo de clicks:</strong> El usuario hace clic en lo que parece ser un enlace inocente, pero ejecuta una acción en tu sitio
  • <strong>Ataques de phishing mejorado:</strong> Combinan clickjacking con ingeniería social para aumentar la efectividad
  • <strong>Ataques de likes automáticos:</strong> Engañan a usuarios para dar like o compartir contenido sin su consentimiento
  • <strong>Ataques de ad fraud:</strong> Generan clics fraudulentos en anuncios para monetizar de forma ilegal
ataque-clickjacking.js
Loading code...

Este código muestra cómo un atacante puede estructurar un ataque de clickjacking básico. El iframe se configura con opacidad cero para hacerlo completamente invisible, pero los elementos dentro del iframe siguen siendo interactivos. El atacante posiciona el iframe exactamente sobre un botón engañoso en su página maliciosa.

Advertencia: El Clickjacking es Invisible

A diferencia de otros ataques de seguridad, el clickjacking no deja rastros visibles en la consola del navegador o en los logs del servidor. El usuario no recibe ningún error ni advertencia, y la acción se ejecuta normalmente desde la perspectiva de tu servidor. Esto hace que sea extremadamente difícil de detectar sin las protecciones adecuadas implementadas.

Protección con X-Frame-Options

X-Frame-Options es un header HTTP con tres valores: DENY bloquea cualquier iframe, SAMEORIGIN permite solo iframes del mismo origen (mismo protocolo, dominio y puerto), y ALLOW-FROM https://dominio.com (obsoleto, no soportado en Chrome/Safari). Configúralo con res.setHeader('X-Frame-Options', 'DENY') en Express o helmet.frameguard({ action: 'deny' }).

X-Frame-Options: DENY

El valor DENY es la opción más restrictiva y segura. Prohíbe cualquier intento de incrustar tu sitio en un frame, independientemente del origen. Esto significa que ni siquiera tu propio sitio puede incrustar páginas de tu dominio en iframes internos. Es la opción recomendada para aplicaciones que no necesitan ser incrustadas en ningún contexto.

x-frame-deny.js
Loading code...

Este ejemplo muestra cómo configurar el header X-Frame-Options con el valor DENY en un servidor Node.js con Express. Una vez implementado, cualquier intento de cargar tu sitio en un iframe será bloqueado por el navegador, mostrando un error de carga del frame.

X-Frame-Options: SAMEORIGIN

El valor SAMEORIGIN permite que tu sitio sea incrustado solo por páginas del mismo origen (mismo protocolo, dominio y puerto). Esta opción es útil si tu aplicación usa iframes internamente para cargar diferentes secciones o componentes, pero quieres prevenir que sitios externos incrusten tu contenido.

x-frame-sameorigin.js
Loading code...

Esta configuración permite que tu propio sitio use iframes internamente mientras bloquea cualquier intento de sitios externos. Es un buen balance entre seguridad y funcionalidad para aplicaciones que necesitan estructurar su contenido en múltiples frames.

X-Frame-Options: ALLOW-FROM

El valor ALLOW-FROM permite especificar qué orígenes específicos pueden incrustar tu sitio. Sin embargo, este valor tiene limitaciones importantes: no es soportado por todos los navegadores modernos (Chrome y Safari lo ignoran) y solo permite un único origen. Por estas razones, se considera obsoleto y se recomienda usar CSP frame-ancestors en su lugar.

x-frame-allow-from.js
Loading code...

Aunque este ejemplo muestra cómo usar ALLOW-FROM, debes tener en cuenta que esta configuración no funcionará en Chrome ni Safari. Para una protección consistente en todos los navegadores, usa CSP frame-ancestors en lugar de X-Frame-Options ALLOW-FROM.

Limitaciones de X-Frame-Options

X-Frame-Options no permite especificar múltiples orígenes permitidos ni usar wildcards. Si necesitas permitir que varios dominios específicos incrusten tu sitio, debes usar CSP frame-ancestors que es más flexible y es el estándar moderno recomendado por todos los navegadores.

Content Security Policy: frame-ancestors

CSP frame-ancestors es el estándar moderno para prevenir clickjacking. Usa frame-ancestors 'none' para bloquear todos los iframes, frame-ancestors 'self' para permitir solo mismo origen, o frame-ancestors 'self' https://socio.com https://app.com para múltiples orígenes específicos. A diferencia de X-Frame-Options, soporta wildcards como https://*.empresa.com y múltiples dominios en una sola directiva.

CSP frame-ancestors: Bloqueo Total

Para bloquear completamente cualquier intento de incrustar tu sitio, usa el valor 'none'. Esto es equivalente a X-Frame-Options: DENY y es la opción más segura para aplicaciones que no necesitan ser incrustadas en ningún contexto. Esta configuración previene clickjacking de manera efectiva en todos los navegadores modernos.

csp-frame-ancestors-none.js
Loading code...

Esta configuración establece la directiva frame-ancestors con el valor 'none', bloqueando cualquier intento de incrustar tu sitio. El navegador rechazará cargar la página en cualquier frame, mostrando un error de violación de CSP en la consola.

CSP frame-ancestors: Mismo Origen

El valor 'self' permite que solo páginas del mismo origen incrusten tu sitio. Esto es equivalente a X-Frame-Options: SAMEORIGIN y es útil cuando tu aplicación usa iframes internamente pero quieres prevenir incrustación externa. Esta configuración mantiene la funcionalidad interna mientras protege contra ataques de sitios externos.

csp-frame-ancestors-self.js
Loading code...

Esta configuración permite que tu propio sitio use iframes internamente mientras bloquea cualquier intento de sitios externos. La directiva frame-ancestors con 'self' es más flexible que X-Frame-Options porque puedes combinarla con otras directivas CSP para una política de seguridad completa.

CSP frame-ancestors: Múltiples Orígenes

Una de las ventajas principales de CSP frame-ancestors sobre X-Frame-Options es la capacidad de especificar múltiples orígenes permitidos. Esto es especialmente útil cuando necesitas que varios dominios específicos incrusten tu sitio, como socios de negocio o aplicaciones internas en diferentes subdominios.

csp-frame-ancestors-multiple.js
Loading code...

Esta configuración permite que múltiples orígenes específicos incrusten tu sitio mientras bloquea todos los demás. Cada origen debe incluir el protocolo https:// y el dominio completo. Para máxima compatibilidad, implementa tanto X-Frame-Options como CSP frame-ancestors: los navegadores modernos respetarán CSP, mientras que navegadores antiguos usarán X-Frame-Options.

Técnicas de Frame-Busting en JavaScript

El frame-busting detecta iframes usando if (window.top !== window.self) y escapa con window.top.location = window.self.location. Aunque CSP y X-Frame-Options son las soluciones preferidas (implementadas por el navegador), el frame-busting proporciona una capa adicional de protección en navegadores antiguos, aunque puede ser bloqueado por atacantes usando sandbox attribute o sobrescribiendo location.

Frame-Busting Básico

La técnica más simple verifica if (window.top !== window.self) para detectar si la página está en un iframe. Si es verdadero, usa window.top.location = window.self.location para redirigir la ventana superior a la URL actual, rompiendo el iframe y cargando tu sitio en la ventana principal del navegador.

frame-busting-basico.js
Loading code...

Este código verifica si window.top (la ventana superior) es diferente de window.self (la ventana actual). Si son diferentes, significa que la página está en un iframe, y el script asigna la URL actual a window.top.location, forzando al navegador a cargar tu sitio en la ventana principal.

Frame-Busting Avanzado con Protección

Los atacantes pueden bloquear el frame-busting sobrescribiendo location. Una técnica robusta usa setInterval(() => { if (window.top !== window.self) advancedFrameBusting() }, 100) para verificar continuamente, y múltiples métodos de escape: window.top.location, window.open(url, '_top'), y manipulación del DOM con link.target = '_top'.

frame-busting-avanzado.js
Loading code...

Este código implementa un frame-busting más robusto que verifica continuamente si la página está en un iframe. Usa múltiples métodos para escapar, incluyendo asignación directa, redirección con window.open y manipulación del DOM. Esto hace más difícil para los atacantes bloquear todas las vías de escape.

Limitaciones del Frame-Busting

El frame-busting en JavaScript puede ser bloqueado por atacantes sofisticados usando técnicas como el sandbox attribute en iframes o manipulando el contexto de ejecución. Además, el frame-busting puede ser molesto para usuarios legítimos si tu aplicación necesita ser incrustada. Por estas razones, usa el frame-busting solo como una capa adicional de protección, no como tu única defensa.

Frame-Busting con Event Listeners

Otra técnica de frame-busting usa event listeners para detectar intentos de cargar la página en un iframe. Esta técnica puede ser más efectiva porque se ejecuta en momentos específicos del ciclo de vida de la página y puede ser más difícil de bloquear para los atacantes.

frame-busting-event-listeners.js
Loading code...

Este código usa event listeners para detectar cuando la página está siendo cargada en un iframe. El evento visibilitychange detecta cambios en la visibilidad de la página, que puede ocurrir cuando se carga en un iframe oculto. El evento focus verifica si la página tiene el foco, lo cual puede indicar que está en un iframe transparente.

Errores Comunes en Prevención

Al implementar protección contra clickjacking, los desarrolladores cometen errores que pueden comprometer la seguridad o causar problemas de funcionalidad. Conocer estos errores comunes te ayudará a evitarlos y a implementar una protección efectiva sin romper la funcionalidad de tu aplicación.

Error 1: Omitir Protección en Páginas Críticas

Un error común es implementar protección contra clickjacking solo en algunas páginas, olvidando otras que son igualmente críticas. Por ejemplo, proteger la página de login pero no la de transferencia de fondos deja una brecha de seguridad significativa. Los atacantes pueden engañar a usuarios ya autenticados para realizar acciones en páginas desprotegidas.

error-omitir-proteccion.js
Loading code...

Este código muestra un error común: implementar protección solo en la ruta de login pero no en otras rutas críticas. Los atacantes pueden saltarse la protección de login y dirigir a usuarios autenticados directamente a páginas desprotegidas para ejecutar acciones maliciosas.

Error 2: Usar Wildcards en CSP frame-ancestors

Usar wildcards como * en CSP frame-ancestors es un error grave que anula completamente la protección contra clickjacking. Aunque puede parecer conveniente para permitir cualquier origen, esto permite que cualquier sitio, incluyendo sitios maliciosos, incruste tu aplicación. Si necesitas permitir múltiples orígenes, especifica cada uno explícitamente.

error-allow-wildcard.js
Loading code...

Este código muestra un error crítico: usar el wildcard * en frame-ancestors. Esta configuración permite que cualquier sitio incruste tu aplicación, eliminando completamente la protección contra clickjacking. En su lugar, especifica cada origen permitido explícitamente.

Error 3: Conflictos entre Headers

Cuando implementas tanto X-Frame-Options como CSP frame-ancestors, es importante que no haya conflictos entre ellos. Por ejemplo, si X-Frame-Options está configurado con DENY pero CSP frame-ancestors permite ciertos orígenes, el resultado puede ser inconsistente entre navegadores y confuso para los desarrolladores.

error-conflicto-headers.js
Loading code...

Este código muestra un error común: configurar X-Frame-Options con DENY pero CSP frame-ancestors con orígenes específicos. Esto crea una configuración inconsistente donde diferentes navegadores pueden comportarse de manera diferente. Asegúrate de que ambos headers sean consistentes en su intención.

Solución: Configuración Consistente

Para evitar conflictos, asegúrate de que X-Frame-Options y CSP frame-ancestors tengan configuraciones consistentes. Si usas DENY en X-Frame-Options, usa 'none' en CSP frame-ancestors. Si usas SAMEORIGIN, usa 'self'. Si necesitas orígenes específicos, omite X-Frame-Options y usa solo CSP frame-ancestors.

Resumen: Prevención de Clickjacking

Conceptos principales:

  • El clickjacking engaña a usuarios para hacer clic en elementos invisibles o disfrazados
  • X-Frame-Options es un header HTTP que controla quién puede incrustar tu sitio en iframes
  • CSP frame-ancestors es la versión moderna y más flexible de X-Frame-Options
  • Las técnicas de frame-busting en JavaScript detectan si tu sitio está en un iframe
  • El frame-busting puede ser bloqueado por atacantes sofisticados, no es una solución completa

Mejores prácticas:

  • Implementa tanto X-Frame-Options como CSP frame-ancestors para máxima compatibilidad
  • Usa DENY o 'none' si tu aplicación no necesita ser incrustada en ningún contexto
  • Especifica orígenes permitidos explícitamente, evita wildcards en CSP frame-ancestors
  • Protege todas las páginas críticas, no solo la página de login
  • Usa frame-busting solo como capa adicional, no como tu única defensa