Autenticación JWT: seguridad sin estado
Sesiones frente a JWTS. El debate es interminable. Cuándo usar tokens web JSON, cómo almacenarlos de forma segura (HttpOnly) y cómo manejar la rotación.
El debate entre sesión y token
Autenticación de sesión (el clásico):
- El usuario inicia sesión.
- El servidor crea una ID de sesión (
sess_123). Lo almacena en Memoria/Redis. - El servidor envía la identificación a la cookie del cliente.
- El cliente envía cookie.
- El servidor busca
sess_123en Redis. Encuentra “El usuario es Bob”. Pros: Revocable (Eliminar de Redis = Usuario cerrado). Desventajas: Con estado. Necesitas Redis. Es más difícil escalar entre regiones.
Autenticación JWT (el moderno):
- El usuario inicia sesión.
- El servidor crea un token web JSON. Contiene
{ id: 1, nombre: "Bob" }. Lo firma con una clave secreta. - El servidor envía JWT al cliente.
- El cliente envía JWT.
- El servidor verifica la firma. Encuentra “El usuario es Bob”. Ventajas: Apátrida. No se requiere búsqueda en la base de datos. “El token es la sesión”. Desventajas: Irrevocable. En caso de robo, válido hasta el vencimiento.
Por qué Maison Code analiza esto
En Maison Code, creamos microservicios y aplicaciones móviles. Las sesiones son terribles para las aplicaciones móviles (las cookies son difíciles). Las sesiones son terribles para los microservicios (compartir una instancia de Redis entre 10 servicios es un acoplamiento). JWT es el pasaporte universal. El Servicio A puede verificar el token emitido por el Servicio de autenticación con solo conocer la clave pública. Implementamos flujos JWT estrictos para habilitar nuestras arquitecturas sin cabeza.
La anatomía de un JWT
Tres partes, separadas por puntos.
Encabezado.Carga útil.Firma.
- Encabezado: Algoritmo (
HS256). - Carga útil: Datos (
sub: 123,exp: 17000000). - Firma:
Hash(Encabezado + Carga útil + Secreto).
Crítico: La carga útil está codificada en Base64, NO cifrada.
Cualquiera puede leerlo (atob(token)).
Regla: Nunca pongas secretos en el JWT. Sin contraseñas. Sin tarjetas de crédito.
Solo ID de usuario y roles públicos.
¿Dónde guardarlo? (La Guerra Santa)
Opción 1: Almacenamiento local
- Fácil (
localStorage.setItem('token', t)). - Vulnerable a XSS. Si el atacante ejecuta JS, lee el token.
Opción 2: Cookie HttpOnly
- Seguro. JS limita el acceso.
- Vulnerable a CSRF.
- Requiere
SameSite=Estricto.
Veredicto del código de Maison: Cookie HttpOnly. CSRF es más fácil de mitigar (SameSite) que XSS. Para aplicaciones móviles, utilice Almacenamiento seguro (llavero).
El patrón de token de acceso/token de actualización
Para solucionar el problema “Irrevocable”.
- Token de acceso: Duración corta (5 minutos). Se utiliza para llamar a API.
- Token de actualización: Larga duración (7 días). Almacenado de forma segura (HttpOnly/DB). Usado solamente para obtener un nuevo token de acceso.
Flujo:
- El cliente realiza una llamada a la API con el token de acceso.
- El servidor dice “401 caducado”.
- El cliente llama a
/refresh-tokencon Refresh Token. - El servidor comprueba la base de datos. “¿Está bloqueado este token de actualización? No.”
- El servidor emite un nuevo token de acceso. Seguridad: Si roban el token de acceso, funciona durante 5 minutos. Si prohíbe al usuario, elimina el token de actualización de la base de datos. Se bloquean en un máximo de 5 minutos.
Algoritmo: HS256 frente a RS256
- HS256 (Simétrico): El servidor firma y verifica con el mismo Secreto. Rápido. Sencillo para monolitos.
- RS256 (Asimétrico): El servicio de autenticación firma con clave privada. Otros Servicios verifican con Clave Pública.
- Esto es crucial para los microservicios. El “Servicio del Producto” puede verificar al usuario sin conocer el secreto del Servicio de Autenticación.
La visión del escéptico
“Las sesiones son más sencillas”. Contrapunto: Sí, para un monolito con un servidor. Intente realizar sesiones cuando tenga una interfaz Next.js en Vercel Edge Network y un backend Go en AWS. La latencia de “Verificar Redis” acaba con el rendimiento de Edge. JWT permite verificar la autenticación en el borde (verificar firma en 1 ms) sin un viaje de base de datos.
Preguntas frecuentes
P: ¿Puedo invalidar manualmente un JWT? R: No. Ese es el punto. Puede implementar una “Lista negra” (almacenar ID de JWT no válidos en Redis), pero luego reinventó las sesiones. Utilice tiempos de vencimiento cortos en su lugar.
P: ¿Qué tamaño puede tener un JWT? R: Mantenlo pequeño. Se envía en cada encabezado HTTP. Si le asigna 50 permisos, ralentizará cada solicitud.
10. Rotación de tokens (la cadena de actualización)
Los tokens de actualización estándar tienen un defecto: si son robados, el ladrón tiene acceso durante 7 días. Solución: Actualizar rotación de tokens.
- El cliente envía el token de actualización A.
- El servidor devuelve el token de acceso B Y nuevo token de actualización C.
- El servidor elimina el token de actualización A. Si el ladrón intenta usar el “Token de actualización antiguo A” nuevamente -> Alarma de seguridad. El servidor detecta la reutilización de un token revocado. Invalida inmediatamente toda la “familia de tokens” (A, C y todos los descendientes). El ladrón queda fuera instantáneamente. Se solicita al usuario real que vuelva a iniciar sesión.
11. JWKS (Conjuntos de claves web JSON)
¿Cómo se rotan las Claves de firma?
No puedes volver a agrupar la clave pública en las aplicaciones todas las semanas.
Solución: Punto final JWKS (/.well-known/jwks.json).
El servidor de autenticación publica sus claves públicas en una URL.
El servidor de recursos los recupera dinámicamente (y los almacena en caché).
Si sospecha que la clave está comprometida, rota la clave en el servidor de autenticación, actualiza el JWKS y todos recogen la nueva clave en 5 minutos.
12. OAuth 2.1 y el futuro
JWT es solo el formato de token. OAuth 2.0 es el flujo. La industria se está moviendo hacia OAuth 2.1 (Consolidación de mejores prácticas).
- No más flujo implícito (token de acceso en hash de URL).
- PKCE (Clave de prueba para intercambio de códigos) es obligatorio. Ahora implementamos PKCE incluso para aplicaciones web del lado del servidor. Previene ataques de “inyección de código de autorización”.
13. Autenticación de máquina a máquina (credenciales de cliente)
¿Qué pasa si “Cron Job A” necesita llamar a “API B”?
No hay ningún usuario. Sin contraseña.
Usamos Flujo de credenciales de cliente.
POST /token {id_cliente, secreto_cliente}.
Devuelve un JWT.
Esto estandariza la autenticación. Ya sea un Usuario (sub: user_1) o una Máquina (sub: service_billing), la API simplemente valida la firma JWT.
La uniformidad es seguridad.
14. Conclusión
JWT es la moneda de la web moderna.
Al igual que el dinero, debe acuñarse con cuidado (Firma), almacenarse de forma segura (Caja fuerte) y comprobarse en busca de falsificaciones (Verificación).
No lances tu propia criptomoneda. Utilice bibliotecas (jsonwebtoken, jose).
¿Autenticación rota?
Si los usuarios cierran sesión de forma aleatoria o si almacena tokens en LocalStorage, Maison Code puede proteger su flujo de autenticación. Implementamos estrategias de rotación OAuth2, OIDC y JWT.
¿Dolores de cabeza con autenticación?
Protegemos la autenticación sin estado mediante JWT (patrón de acceso/actualización) y cookies HttpOnly para proteger las identidades en los microservicios. Contrata a nuestros Arquitectos.