AldeaCode Logo
Security Zero Trust en Frontend: Patrón BFF y DPoP con OAuth
Security AldeaCode Architecture

Zero Trust en Frontend: Patrón BFF y DPoP con OAuth

Deja de guardar access tokens de OAuth en el navegador. El patrón Backend for Frontend (BFF) y DPoP explicados, con ejemplos de código.

Durante años tratamos al backend como una fortaleza y al navegador como un ayudante de confianza. Eso funcionaba cuando casi todos los ataques venían de fuera. Ya no funciona.

El navegador corre en la máquina de otra persona. Ejecuta tu código junto a extensiones, scripts de anuncios y lo que sea que el usuario tenga instalado. Cuando aceptas eso, la pregunta de seguridad cambia. Ya no es “cómo evitamos que entren los atacantes”, sino “qué pasa cuando un atacante ya está dentro de la página”.

A esa mentalidad la llaman Zero Trust. La versión corta es, no asumas que ningún entorno es seguro. Verifica cada petición, cada script, cada token, siempre.

Por qué “confía menos en el navegador” pasó a ser lo normal

Cambiaron dos cosas.

La primera son los ataques a la cadena de suministro (supply chain). Una librería de la que dependes se ve comprometida. Tu app empieza a servir código malicioso, firmado por ti, ejecutándose con acceso completo a la página. El usuario no hizo nada raro. Tu CI no hizo nada raro. El código simplemente está envenenado.

La segunda es el XSS, que sigue apareciendo aunque uses un framework moderno. Los frameworks escapan la salida por defecto, lo cual ayuda. Pero un dangerouslySetInnerHTML descuidado, un widget de terceros, una dependencia vieja, y de repente un script puede leer todo lo que la página puede leer. Incluidos tus tokens.

Así que la pregunta no es “podemos evitar todo XSS”. La pregunta es “cuando ocurra un XSS, cuánto daño puede hacer”.

Bearer tokens, el problema de la entrada de cine

La mayoría de las apps usan hoy bearer tokens. El nombre es honesto. Quien lleve el token, lo puede usar. El servidor no comprueba quién eres, comprueba qué tienes.

Piénsalo como una entrada de cine. Al cine no le importa si tú la compraste. Le importa que la entrada sea válida. Si se te cae en el aparcamiento y alguien la recoge, esa persona entra.

Un bearer token robado es una sesión robada. Si un script malicioso lee tu localStorage y encuentra un access token, puede llamar a tu API como tú, desde donde quiera, hasta que el token expire. No hay segundo factor. El token es el factor.

Esta es la parte que rompe la intuición. El token no está “atado” a nada. Ni al dispositivo, ni al navegador, ni a la IP. Es solo una cadena de texto en la que el servidor confía.

DPoP en palabras claras

DPoP significa Demonstrating Proof of Possession (demostración de posesión). El estándar es corto. La idea es aún más corta.

Cada dispositivo genera su propio par de claves. La clave privada nunca sale del dispositivo. Cuando el navegador hace una petición, firma esa petición concreta con la clave privada y manda la firma junto al token. El servidor comprueba dos cosas, que el token es válido y que la firma viene de la clave que está atada a ese token.

Ahora robar el token solo no basta. Para usarlo, el atacante también necesita la clave privada. Y la clave privada vive en almacenamiento no extraíble (CryptoKey con extractable: false, o claves respaldadas por hardware en algunas plataformas). Un script que lee la página no puede copiarla.

DPoP no arregla el XSS. Cambia lo que un XSS puede hacer. Un atacante que ejecuta código en tu página todavía puede llamar a tu API mientras el usuario está en la página, pero ya no puede llevarse la sesión y usarla luego desde su propia máquina. Esa es una rebaja real de la capacidad del atacante.

BFF en palabras claras

DPoP sube el listón. El patrón Backend For Frontend, abreviado BFF, elimina el problema entero.

La idea es esta. Montas un backend pequeño que vive entre el navegador y tu API real o tu proveedor OAuth. El navegador solo habla con ese backend pequeño. El navegador nunca ve un access token de OAuth. Ni en localStorage, ni en memoria, ni en ningún sitio.

Lo que el navegador guarda es una cookie de sesión. La cookie es HttpOnly, así que JavaScript no puede leerla. Es Secure, así que solo viaja por HTTPS. Es SameSite=Strict o Lax, así que no se filtra a otros sitios. El valor de la cookie es opaco, normalmente una referencia cifrada.

Cuando el usuario hace algo, el navegador manda la cookie al BFF. El BFF busca el token real en el servidor, llama a la API de arriba y devuelve el resultado. El token nunca cruza al navegador.

El trato es simple. Añades un salto extra y un servicio pequeño con estado. Eliminas una categoría entera de ataques (robo de tokens vía JS) a cambio.

Lo que solemos recomendar

Para la mayoría de las apps, ve directo a BFF. Si tu equipo controla un backend o una edge function, no hay buena razón para meter access tokens de OAuth en el navegador. El patrón “SPA pura con tokens en localStorage” tenía sentido cuando montar serverless era doloroso. Ya no lo es.

Usa DPoP cuando de verdad no puedes tener backend, o cuando necesitas bearer tokens para una API pública. Es la respuesta correcta para esos casos. No es el valor por defecto correcto.

Pon Content Security Policy (CSP) encima de todo. CSP es la diferencia entre “ocurrió un XSS” y “ocurrió un XSS y el script no pudo hacer nada útil”. Nuestra guía de CSP repasa una política estricta que puedes desplegar.

Una lista corta para el caso común:

  1. Los tokens de OAuth viven en el servidor, detrás de un BFF.
  2. El navegador solo guarda una cookie de sesión, HttpOnly más Secure más SameSite.
  3. CSP estricta con nonces, sin unsafe-inline, sin orígenes comodín.
  4. Si tienes que guardar tokens en el navegador, usa DPoP, no bearer pelado.
  5. Audita las cabeceras reales en producción, no solo en local. Nosotros usamos nuestro SEO Expert para detectar regresiones.
  6. Combina esto con seguridad técnica LOPD si trabajas con datos de usuarios en España.

El objetivo no es hacer imposible el XSS. El objetivo es que un XSS exitoso resulte aburrido.

Lo que hacemos

Webs honestas, sin atajos.

Ingeniería real y diseño cuidado. Si te ha gustado el post, hablemos del tuyo.

Hablemos →

Te puede interesar

Ver todos los artículos →