Llaves de acceso dentro de iframes

Para proporcionar una autenticación fluida y contextual en varios dominios, las organizaciones suelen incorporar páginas de acceso en elementos iframe. Sin embargo, cargar contextos de autenticación dentro de marcos de terceros expone a los usuarios a amenazas críticas, como el clickjacking (suplantación de la IU) y la creación no autorizada de credenciales. Para mitigar estos riesgos, los navegadores inhabilitan WebAuthn en los elementos iframe de origen cruzado de forma predeterminada. Para levantar esta restricción de forma segura, se requieren protocolos activos de defensa en profundidad.

Identifica modelos de amenazas

Antes de habilitar las llaves de acceso (WebAuthn) dentro de los submarcos, comprende las situaciones de abuso contra las que te defiendes:

  • Seguimiento con la inserción de un iframe oculto: Un atacante activa un mensaje de WebAuthn desde su propio dominio con un anuncio o widget en un sitio de confianza, lo que engaña a los usuarios para que autoricen una llave de acceso sin ver el contexto. Esto vincula la identidad del usuario a una cuenta controlada por el atacante para recopilar datos.
  • Superposición visual y clickjacking (revestimiento de la IU): Una página principal maliciosa renderiza el iframe de autenticación invisible con CSS estándar y superpone un elemento de IU falso para robar un clic que activa un flujo de autenticación. Esto puede provocar el secuestro de la sesión o acciones no autorizadas forzadas si el usuario completa la instrucción sin querer.

Para contrarrestar estas amenazas, sigue estas prácticas recomendadas:

Para el documento de nivel superior (marco superior):

Para el documento incorporado (iframe):

Para ambos documentos:

Habilita la delegación con la Política de permisos

De forma predeterminada, los navegadores bloquean el acceso a WebAuthn en iframes de origen cruzado. La Política de permisos es el mecanismo unificado de la plataforma web que permite que un documento de nivel superior delegue de forma explícita estas potentes capacidades a orígenes de terceros específicos y confiables.

Tokens de funciones

WebAuthn usa dos tokens distintos:

  • publickey-credentials-get: Otorga autorización para los flujos de acceso con llave de acceso (navigator.credentials.get()).
  • publickey-credentials-create: Otorga autorización para los flujos de registro de llaves de acceso (navigator.credentials.create()).

Requisitos para habilitar la función

Para habilitar estas capacidades, se requiere alineación tanto en la respuesta del servidor principal como en el lenguaje de marcado del cliente:

Permissions-Policy: publickey-credentials-get=(self "https://embedded-auth.example.com")

Política de Permisos: Compatibilidad con publickey-credentials-get:

Browser Support

  • Chrome: 88.
  • Edge: 88.
  • Firefox: not supported.
  • Safari: not supported.

Source

Política de Permisos: Compatibilidad con publickey-credentials-create:

Browser Support

  • Chrome: 88.
  • Edge: 88.
  • Firefox: not supported.
  • Safari: not supported.

Source

  • El atributo allow de HTML: En el lenguaje de marcado HTML, el elemento <iframe> también debe declarar que habilita la función.
<iframe src="https://embedded-auth.example.com?nonce=deadbeef12345678&client=https%3A%2F%2Fembedded-auth.example.com" allow="publickey-credentials-get"></iframe>

Compatibilidad con iframe allow="publickey-credentials-get":

Browser Support

  • Chrome: 84.
  • Edge: 84.
  • Firefox: 118.
  • Safari: not supported.

Compatibilidad con iframe allow="publickey-credentials-create":

Browser Support

  • Chrome: not supported.
  • Edge: not supported.
  • Firefox: 123.
  • Safari: not supported.

Habilita las cookies de terceros particionadas

Para garantizar un flujo de autenticación confiable, se debe establecer y mantener una sesión dentro del iframe incorporado de origen cruzado. A medida que los navegadores modernos pasaron a tener restricciones estrictas para las cookies de terceros, los mecanismos de persistencia estándar suelen bloquearse de forma predeterminada y es posible que debas llamar a la API de Storage Access para obtener acceso.

Para mitigar estos obstáculos, configura tus cookies de sesión con los atributos SameSite: None, Secure y Partitioned. Este mecanismo de plataforma unificada garantiza un estado persistente dentro del iframe y, al mismo tiempo, respeta los controles de privacidad a nivel del navegador.

Conjunto SameSite: None

SameSite: None marca explícitamente una cookie para el acceso entre sitios, lo que permite que se envíe con solicitudes realizadas desde un contexto de terceros (como un iframe). Este atributo es un requisito previo para que las cookies funcionen en situaciones de varios orígenes, aunque debe combinarse con el atributo Secure para que los navegadores modernos lo acepten.

Conjunto Partitioned

El atributo Partitioned habilita la cookie para CHIPS (Cookies Having Independent Partitioned State), lo que permite que la cookie se almacene por separado para cada sitio de nivel superior. Esto garantiza que la cookie siga siendo accesible dentro del contexto específico del iframe de terceros, lo que permite un estado de sesión persistente sin habilitar el seguimiento entre sitios. El usuario deberá volver a acceder a cada incorporación en un sitio diferente.

Protege el endpoint con la Política de Seguridad del Contenido

Si bien la Política de Permisos determina si tu iframe puede ejecutar WebAuthn, la Política de Seguridad del Contenido (CSP) determina quién puede alojar tu iframe.

En el caso de un endpoint de autenticación, es fundamental garantizar que solo los sitios de socios autorizados o tus propias propiedades puedan cargar el submarco de inicio de sesión, lo que detiene los intentos de clickjacking no autorizados antes de que puedan cargar la IU.

Usa frame-ancestors

La directivaframe-ancestors define las páginas principales válidas que pueden incorporar tu sitio. Si agregas dominios a esta directiva, puedes permitir los dominios que pueden incorporar el submarco de inicio de sesión.

Content-Security-Policy: frame-ancestors 'self' https://parent-site.example.com;

Política de seguridad del contenido: Compatibilidad con frame-ancestors:

Browser Support

  • Chrome: 40.
  • Edge: 15.
  • Firefox: 58.
  • Safari: 10.

Source

Conjunto X-Frame-Options

El encabezado X-Frame-Options heredado admite una capacidad similar, pero solo admite opciones binarias (DENY o SAMEORIGIN). Establece CSP frame-ancestors y X-Frame-Options: DENY en caso de que el navegador no admita CSP. El CSP siempre tiene prioridad cuando se admite.

X-Frame-Options: DENY

Compatibilidad con X-Frame-Options:

Browser Support

  • Chrome: 4.
  • Edge: 12.
  • Firefox: 4.
  • Safari: 4.

Source

Confía, pero verifica en el servidor

Las verificaciones del cliente del navegador evalúan la intención y los permisos, pero el servidor es el árbitro final de la confianza. Verifica la respuesta en el servidor de la parte que confía (RP) para asegurarte de que el contexto sea válido y esté firmado.

Carga útil de datos del cliente

Los datos del cliente de WebAuthn incluyen parámetros diseñados específicamente para ayudarte a verificar el contexto de una solicitud realizada dentro de un iframe:

  • crossOrigin (booleano): Indica si se invocó la API de WebAuthn dentro de un iframe de origen cruzado. Si tu arquitectura depende de los elementos iframe, tu servidor debe aplicar que esta marca sea true.
  • topOrigin (cadena): Es el origen del contexto de navegación de nivel superior (lo que se ve en la barra de direcciones del navegador). El servidor debe verificar esto en una lista de orígenes principales conocidos y autorizados.

Lista de verificación

Para verificar la respuesta del autenticador en tu servidor, sigue estos pasos:

  1. Analiza y decodifica el collectedClientData firmado de la respuesta del autenticador.
  2. Asegúrate de que type coincida con la ceremonia (webauthn.get o webauthn.create).
  3. Verifica la presencia y la firma del usuario.
  4. Si la solicitud se envió desde una estructura de iframe, haz lo siguiente:
    • Aplica crossOrigin === true.
    • Asegúrate de que topOrigin coincida con tu lista autorizada de orígenes principales.

Establece sesiones de forma segura con postMessage()

Para establecer una sesión de forma confiable, el iframe debe pasar el token de autenticación a la página principal con postMessage(), lo que permite que la página principal administre el estado de la sesión en su propio contexto propio.

Flujo de trabajo seguro

Para establecer una sesión segura, sigue este flujo de trabajo:

  1. Asegúrate de que la URL del iframe src contenga los parámetros de consulta nonce y origin:
    • Usa un valor aleatorio para nonce. Un nonce funciona como un token de verificación de seguridad para garantizar que el token de autenticación recibido de un iframe coincida de forma legítima con la sesión específica que inició la página principal.
    • Usa el dominio del iframe principal para origin. Un parámetro origin especifica el origen de la página principal, lo que permite que el iframe identifique de forma segura el contexto autorizado en el que se incorporó.
  2. El iframe completa la autenticación de WebAuthn con su propio servidor.
  3. El servidor de iframe emite un token, como un JWT, que incluye el nonce y reenvía a la página principal.

    // Extract nonce and origin from the URL params
    const urlParams = new URLSearchParams(window.location.search);
    const nonce = urlParams.get('nonce');
    const origin = urlParams.get('origin');
    if (!nonce || !origin) {
      alert('Nonce or origin is missing in the URL');
      return;
    }
    
    // Create a JWT
    const response = await post('/createToken', { nonce, origin });
    const token = response.token;
    
    // Post the JWT to the parent frame
    window.parent.postMessage({ token }, origin);
    
  4. La página superior escucha el evento message, valida el origen del remitente y verifica el token.

    window.addEventListener("message", (event) => {
      if (event.origin !== "https://embedded-auth.example.com") return;
      // Verify the received JWT
      const result = await post('/verifyIdToken', {
        token: event.data.token,
        origin: provider.origin,
      });
    });
    
  5. La página principal conserva la sesión si el JWT se verifica correctamente.

El remitente y el destinatario comparten responsabilidades de seguridad:

  • El remitente (iframe): Siempre especifica un origen de destino estricto cuando envíes mensajes (nunca uses "*").
  • El receptor (elemento superior): Siempre verifica event.origin cuando recibas mensajes para evitar la suplantación de origen.

Conclusión

El uso seguro de iframe depende de la Política de permisos para la habilitación, la CSP para la restricción, las cookies de terceros particionadas para la persistencia de la sesión, la verificación del servidor del contexto del cliente y la transferencia de sesión adaptada al contexto con postMessage().

Para obtener más información sobre temas relacionados, sigue el blog para desarrolladores de Chrome de Google y explora más recursos en la documentación sobre la identidad del desarrollador de Chrome.