TrendVidStream trasladó todo su stack de autenticación a un sistema de tokens de actualización rotativos que detecta un token robado en el momento en que se reutiliza. El cambio transformó un JWT de 30 días, que antes permitía a un atacante moverse libremente, en una credencial de corta duración que activa un bloqueo instantáneo.

Una sola brecha de seguridad forzó el cambio: un SDK de un socio almacenó en caché un JWT de 30 días en texto plano; un atacante lo extrajo y lo replicó desde otro país, y el único remedio fue rotar la clave de firma, una operación que cerró la sesión de todos los usuarios. El incidente redefinió la seguridad de tokens de la empresa y ahora es la base de su servicio de streaming de vídeo.

Por qué falló el modelo anterior

Los JWT (JSON Web Tokens) son blobs firmados y autónomos que permiten a un servidor verificar una solicitud sin realizar una consulta a la base de datos. Esta conveniencia tiene un coste: si un token dura semanas, robarlo le otorga a un adversario semanas de acceso. En la brecha de seguridad, el token robado permaneció válido hasta su vencimiento de 30 días porque no había forma de invalidarlo de forma anticipada.

Rotar la clave de firma es la única forma global de invalidar todos los tokens, pero obliga a cada usuario a iniciar sesión de nuevo, interrumpiendo el servicio y socavando la confianza. El fallo no residía en el JWT en sí, sino en la dependencia de una única credencial de larga duración.

El nuevo diseño en pocas palabras

TrendVidStream ahora emite dos tipos de tokens:

  • Tokens de acceso – válidos durante 15 minutos; contienen los permisos necesarios para cada llamada a la API.
  • Tokens de actualización (refresh tokens) – tokens de un solo uso que intercambian un token de acceso de corta duración por un nuevo par.

Cuando un cliente presenta un token de actualización, el servidor:

  1. Verifica la firma y los claims del token.
  2. Comprueba si el token ya ha sido utilizado.
  3. Si la comprobación es positiva, emite un nuevo token de actualización y un nuevo token de acceso de 15 minutos.
  4. Marca el antiguo token de actualización como utilizado.

Si el paso 2 falla —es decir, si el mismo token aparece por segunda vez— el servidor lo trata como una señal de robo y revoca toda la «familia» de tokens vinculados a esa sesión de inicio de sesión. Tanto la víctima como el atacante se ven obligados a iniciar sesión de nuevo, cortando el acceso del atacante de inmediato.

Seguimiento de las familias de tokens

En lugar de tratar cada token como un registro aislado, el sistema los agrupa en familias que comienzan cuando un usuario inicia sesión. Cada rotación crea un nuevo miembro de esa familia. El esquema de SQLite utilizado en producción almacena:

  • family_id – un identificador estable para toda la sesión.
  • token_id – un identificador único para cada token de actualización.
  • generation – un contador útil para la depuración.
  • used_at – la marca de tiempo de cuándo se canjeó el token.
  • revoked – un indicador que, al activarse, deshabilita toda la familia.

Cuando se detecta un evento de reutilización, se activa el indicador revoked para ese family_id, invalidando instantáneamente cada token perteneciente a la sesión comprometida. Esto convierte un secuestro silencioso en una alerta de alta señal que aparece en los registros.

Tres reglas de implementación que mantienen la fiabilidad del sistema

  1. Validar la firma primero Un atacante podría adivinar los IDs de los tokens y provocar revocaciones masivas si el servidor comprueba si ha sido «usado antes» antes de verificar la firma. Confirmar la autenticidad primero evita ataques de denegación de servicio innecesarios.

  2. Permitir una ventana de gracia Las aplicaciones móviles a menudo lanzan dos solicitudes de actualización en rápida sucesión cuando un token caduca. Si el servidor es demasiado estricto, la segunda solicitud sería marcada como reutilización, cerrando la sesión de un usuario legítimo. Unos pocos segundos de margen permiten que un token «usado» todavía devuelva un par nuevo, suavizando las condiciones de carrera.

  3. Envolver todo el flujo en una transacción con bloqueo de filas Sin atomicidad, dos solicitudes concurrentes podrían pensar que ambas son las primeras en usar un token, emitiendo tokens de actualización duplicados y rompiendo la garantía de un solo uso. Una transacción de base de datos que bloquee la fila del token garantiza que solo una solicitud tenga éxito.

Conclusión: Al hacer que los tokens de actualización sean de un solo uso y vigilar su reutilización, un sistema puede convertir cada credencial robada en una alarma, protegiendo a los usuarios sin forzar un cierre de sesión en toda la plataforma.