Un token firmado que un servidor te entrega tras iniciar sesión: eso es un JWT. Aquí tienes cómo funciona y los puntos clave para usarlo de forma segura (sin pasos de ataque).
Cómo funciona (tres partes)
Un JWT es encabezado.carga_útil.firma unido con .. El encabezado y la carga útil están solo codificados en base64url (no cifrados); únicamente la firma se hace con una clave secreta (o compartida).
1. encabezado
metadatos como el algoritmo de firma. cualquiera puede leerlo
2. carga útil
claims como el id de usuario y la expiración. cualquiera puede leer = sin secretos
3. firma
hecha con una clave. garantiza que no hay manipulación
El punto clave es que el contenido es "solo legible". Así que la carga útil no es lugar para contraseñas ni datos personales. El valor de un JWT es que, al verificar la firma, el servidor puede confirmar "yo emití este token y no ha sido alterado".
Defensas para usarlo de forma segura
Verifica siempre la firma y fija el alg (lo más importante)
Verifica la firma en el servidor cada vez. Fija el algoritmo de firma (alg) aceptado al valor que esperas y rechaza alg:none (sin firma). No confíes a ciegas en el alg escrito en el encabezado del token.
El RFC de buenas prácticas de JWT (RFC 8725) establece que las bibliotecas DEBEN permitir a quien las llama especificar un conjunto de algoritmos admitidos y NO DEBEN usar ningún otro, y que todas las operaciones criptográficas del JWT deben validarse, rechazando el JWT entero si alguna falla.
Bloquea el cambio de algoritmo, no solo alg:none
Abusar de alg no consiste solo en quitar la firma. El RFC 8725 describe cómo, al cambiar RS256 por HS256, algunas bibliotecas intentan validar la firma con HMAC-SHA256 usando la clave pública RSA como secreto compartido (confusión de algoritmos). La clave pública es, por definición, pública: si eso funciona, un atacante puede crear él mismo una firma válida.
La defensa es la misma: no confíes en el alg del encabezado; fija el algoritmo que espera el servidor. Decide primero el algoritmo y luego verifica, en lugar de elegir la rutina de verificación según el material de clave que te entregan.
Valida para quién es y hasta cuándo
Una firma válida solo te dice que el token es genuino. Si es para ti y sigue vigente es una comprobación aparte.
El RFC 7519 establece que, para aud, si quien procesa el claim no se identifica con un valor del claim «aud» cuando este está presente, el JWT DEBE rechazarse, y que exp identifica el momento de expiración a partir del cual el JWT NO DEBE aceptarse para su procesamiento (nbf funciona igual en el otro extremo). El RFC 8725 añade que la aplicación debe validar que las claves usadas pertenecen al emisor. Que tu servicio acepte un token emitido legítimamente para otro servicio es justo lo que ocurre si te saltas estas comprobaciones.
No pongas secretos en la carga útil
Asume que cualquiera puede decodificar el contenido. Mantén los secretos —contraseñas, datos personales, claves de API— fuera de él. Incluye solo claims que no sean catastróficos si se ven, como identificadores y permisos.
Mantén la expiración corta y ten forma de revocar
Haz que los tokens de acceso sean de vida corta. Una expiración demasiado larga alarga la ventana de daño si uno es robado. Si necesitas revocación, combina un token de acceso de vida corta con un refresh/sesión gestionado en el servidor.
Usa una clave fuerte y guárdala donde no puedan robarla
Haz la clave de firma fuerte, no la reutilices y guárdala en un lugar seguro del servidor. Protege también el token en sí: evita que sea robado vía XSS, llévalo en una cookie con atributos seguros (→ cuidado con el robo vía XSS).
La opinión de este sitio: un JWT no es un mecanismo de sesión para cualquier uso
Los JWT se usan a menudo para "mantener el estado de inicio de sesión", pero tienen una debilidad: la revocación es difícil. Si la firma es válida y no ha expirado, el servidor básicamente confía en él, así que hacer que un cierre de sesión o una invalidación surta efecto de inmediato resulta incómodo.
Nuestra postura es: no uses un JWT como sustituto de larga duración de una sesión. Combina un token de acceso de vida corta con un refresh/sesión revocable en el servidor, y obtienes tanto la ventaja del JWT (verificación barata) como una revocación fácil.
Cuidado: "se decodificó" no es "es válido"
Como el contenido es legible, cualquiera puede pegar un JWT en un decodificador de JWT e inspeccionar el encabezado y la carga útil. Pero decodificar (leer el contenido) y verificar (confirmar la autenticidad) son cosas completamente distintas.
Poder decodificar no garantiza nada sobre la validez. Lo que decide si un token es genuino es siempre la verificación de la firma en el servidor. Leer el contenido para depurar es práctico: solo no confundas "pude leerlo" con "es válido".
JWT frente a sesiones en el servidor (cookies)
Un JWT es solo una forma de mantener el estado de inicio de sesión, y en unos casos encaja mejor que una sesión en el servidor y en otros peor.
| Aspecto | Sesión en el servidor (cookie) | JWT |
|---|---|---|
| Dónde vive el estado | En el servidor (BD o memoria); la cookie solo lleva un ID | Dentro del token; el servidor solo verifica la firma |
| Cierre de sesión / suspensión | Borrarla en el servidor surte efecto de inmediato | Se acepta hasta que expira, salvo que construyas la revocación aparte |
| Visibilidad del contenido | Solo un ID, nada que leer | Cualquiera puede decodificarlo y leerlo |
| Cuándo encaja | Una aplicación web normal en un solo dominio | Varios servicios o API que verifican el mismo token |
Para una aplicación web que vive en un solo dominio, una sesión en el servidor suele bastar. Si eliges JWT, emitir un token nuevo al iniciar sesión sigue el mismo principio que la defensa contra la fijación de sesión.
Un caso real: una biblioteca que no comprobaba el algoritmo
El cambio de algoritmo no es solo teoría. La biblioteca de JWT para Node.js jsonwebtoken, muy usada, en sus versiones 4.2.1 y anteriores no comprobaba correctamente el algoritmo indicado en el encabezado del token, lo que permitía saltarse la verificación (CVE-2015-9235, corregido en 4.2.2). Un token que debía verificarse con una clave pública podía firmarse con un algoritmo de clave compartida (familia HS) y aun así pasar: exactamente la confusión de algoritmos descrita arriba.
Dos lecciones:
- Mantén la biblioteca actualizada. Puedes revisar tus dependencias en busca de vulnerabilidades conocidas pegando un archivo de bloqueo (lockfile) en el escáner de vulnerabilidades de dependencias
- No dependas de los valores por defecto de la biblioteca; indica el algoritmo aceptado de forma explícita en tu propio código
Cómo revisar tus propios tokens
Pega un token emitido por tu propio servicio en el decodificador de JWT y revisa lo siguiente. La decodificación se hace en tu navegador y el token no se envía a ningún servidor. Aun así, con un token real de producción conviene cerrar sesión después para invalidarlo.
- ¿El
algdel encabezado es el que esperas (y nonone)? - ¿Hay un
exp, y la duración desdeiat(momento de emisión) es adecuada para el uso? - ¿Están
audeisspresentes y apuntan a tu servicio? - ¿Contiene la carga útil algo que no querrías que se viera: direcciones de correo, números de teléfono, claves de API?
Comprueba la verificación en el servidor con pruebas en un entorno de desarrollo. Un token con un carácter de la firma cambiado, un token con otro alg, un token expirado y un token destinado a otro servicio (aud incorrecto) deben rechazarse todos: mantenlo como prueba automatizada.
Fuentes (primarias)
- IETF RFC 8725, «JSON Web Token Best Current Practices» (en inglés) — datatracker.ietf.org (fijar los algoritmos, validar cada operación criptográfica, confusión de algoritmos, claves que pertenecen al emisor)
- IETF RFC 7519, «JSON Web Token (JWT)» (en inglés) — datatracker.ietf.org (las definiciones normativas de
exp,nbfyaud) - GitHub Advisory Database, «Verification Bypass in jsonwebtoken» (CVE-2015-9235, en inglés) — github.com/advisories (afecta a 4.2.1 y anteriores; corregido en 4.2.2)
- OWASP HTML5 Security Cheat Sheet (en inglés) — cheatsheetseries.owasp.org (no guardar identificadores de sesión en localStorage)
Sigue leyendo
- Herramienta: Decodificador / inspector de JWT (comprueba el contenido, detecta alg:none o la expiración)
- Diseño: fallos de diseño en el restablecimiento de contraseñas (en inglés; errores habituales en el lado que emite tokens)
- Glosario: qué es XSS (una vía clásica de robo de tokens) / qué es CSRF / qué es la fijación de sesión / qué es la criptografía de clave pública
- Aprende: elegir bien la MFA