JSON Web Tokens: claims, firmas y validación segura

Actualizado:

Un JSON Web Token, o JWT, es un breve fragmento de texto que transporta algunos datos sobre un usuario o una solicitud —quién lo emitió, a quién se refiere, cuándo caduca— junto con una firma que permite al destinatario comprobar que nadie los ha modificado. Las API aceptan JWT como tokens de acceso, los sistemas de inicio de sesión los emiten como tokens de ID y muchas aplicaciones web los usan como tokens de sesión. Esta guía explica qué contiene un JWT, en qué se diferencian los algoritmos de firma, qué tiene que comprobar un servidor antes de fiarse de un token, los ataques que aprovechan las comprobaciones omitidas y cómo gestionar la caducidad y el almacenamiento.

Qué es un JWT

El formato se define en el RFC 7519, publicado en 2015. Un JWT es un conjunto de claims —afirmaciones como «el sujeto es user-4821»— que se codifica en JSON y después se firma o se cifra. Casi todos los JWT en uso están firmados, como JSON Web Signature (JWS, RFC 7515): cualquiera puede leerlos, pero nadie que no tenga la clave puede modificarlos sin que se note. Los JWT cifrados (JWE, RFC 7516) ocultan además el contenido y son mucho menos habituales.

Dos normas definen los JWT que es más probable que encuentres. OpenID Connect, la capa de inicio de sesión que usan los botones «Iniciar sesión con…», emite un token de ID que debe ser un JWT firmado. El perfil JWT para tokens de acceso de OAuth 2.0 (RFC 9068, 2021) define cómo es un token de acceso de una API cuando se trata de un JWT, aunque los tokens de acceso de muchos proveedores son anteriores a él y no lo siguen al pie de la letra.

Las tres partes

Un JWT firmado está formado por tres fragmentos de texto Base64URL unidos por puntos: una cabecera, una carga útil y una firma. Este es un token completo, dividido por los puntos:

Base64URL es el Base64 de siempre con dos caracteres sustituidos para que el resultado sea seguro en las URL (- y _ en lugar de + y /), y en los JWT se elimina el relleno = del final. Es una codificación, no un cifrado: cualquiera puede volver a convertir las dos primeras partes en JSON.

La firma se calcula sobre el texto exacto de la cabecera y la carga útil codificadas, unidas por el punto que las separa. Basta cambiar un carácter de cualquiera de las dos —retrasar la caducidad, poner a otra persona como sujeto— para que la firma deje de coincidir.

Abrir el decodificador JWT

Los claims estándar

El RFC 7519 define siete claims registrados. Todos son opcionales en la norma básica; los protocolos construidos sobre ella hacen obligatorios algunos de ellos.

ClaimNombreSignificado
issEmisorQuién creó y firmó el token; suele ser una URL
subSujetoA quién se refiere el token; suele ser un ID de usuario
audAudienciaA quién va destinado el token; un destinatario que no figure en la lista debe rechazarlo
expMomento de caducidadA partir de este momento, el token no debe aceptarse
nbfNo antes deAntes de este momento, el token no debe aceptarse
iatMomento de emisiónCuándo se creó el token, lo que indica su antigüedad
jtiID del JWTUn identificador único, útil contra la reutilización (replay) y para la revocación

Los tres claims de tiempo (exp, nbf e iat) son marcas de tiempo Unix: números de segundos, normalmente enteros, transcurridos desde el 1 de enero de 1970 UTC. En el ejemplo, 1791968400 corresponde al 14 de octubre de 2026 a las 09:00 UTC, y 1791969300, a quince minutos después. El RFC 7519 permite que un servidor conceda un pequeño margen a los relojes que no coinciden, «normalmente no más de unos pocos minutos».

Un token de ID de OpenID Connect debe contener iss, sub, aud, exp e iat. Un token de acceso conforme al RFC 9068 debe contener iss, exp, aud, sub, client_id, iat y jti, y llevar el tipo at+jwt en su cabecera. Todo lo demás que haya en una carga útil —un nombre, una dirección de correo electrónico, roles— lo define la aplicación.

Algoritmos de firma

El valor alg de la cabecera indica el algoritmo. Los algoritmos se dividen en dos grupos: los de secreto compartido, en los que la misma clave firma y comprueba, y los de par de claves, en los que una clave privada firma y una clave pública comprueba.

algMétodoQuién puede comprobarloNotas
HS256HMAC con SHA-256 y un secreto compartidoCualquiera que tenga el secreto, que entonces también puede crear tokensLa clave debe tener al menos 256 bits (RFC 7518) y nunca debe ser una contraseña (RFC 8725).
RS256Firma RSA con SHA-256Cualquiera que tenga la clave públicaClaves RSA de al menos 2048 bits. Es la opción por defecto sugerida para los tokens de ID de OpenID Connect.
ES256ECDSA sobre la curva P-256 con SHA-256Cualquiera que tenga la clave públicaClaves y firmas mucho más cortas que con RSA.
Ed25519EdDSA con el conjunto de parámetros Ed25519Cualquiera que tenga la clave públicaRegistrado en octubre de 2025 (RFC 9864); sustituye al identificador EdDSA, más antiguo y menos específico, que muchas bibliotecas siguen usando.
noneSin firmaNadie: el token no está protegidoLas implementaciones no deben aceptarlo por defecto (RFC 7518).

HS256 es adecuado para un único servicio que emite y comprueba sus propios tokens. Cuando otros servicios necesitan comprobar los tokens —varias API o socios externos—, conviene más un par de claves: esos servicios solo reciben la clave pública, que no sirve para crear tokens.

Decodificar no es verificar

Cualquiera puede escribir un JWT con cualquier carga útil; decodificarlo, como hace el decodificador de nTools, solo muestra lo que afirma. Un token solo demuestra algo cuando el servidor que lo recibe ha hecho estas comprobaciones, que siguen el RFC 7519 y el RFC 8725, las prácticas recomendadas actuales del IETF para los JWT:

  1. Fija los algoritmos aceptados en la configuración del servidor y rechaza todos los demás. No dejes nunca que el alg de la cabecera del token decida cómo se comprueba, y usa cada clave con un solo algoritmo.
  2. Verifica la firma con la clave que pertenece al emisor esperado.
  3. Comprueba que iss es el emisor en el que confías y que aud nombra a tu servicio.
  4. Exige que estén presentes exp y los demás claims de los que depende tu servicio, y compara exp y nbf con la hora actual con un margen de unos pocos minutos como máximo. De lo contrario, un token sin exp no caducaría nunca.
  5. Cuando haya un tipo de token definido, comprueba typ —por ejemplo, at+jwt en los tokens de acceso— para que no se pueda hacer pasar un tipo de token por otro.
  6. Trata los campos de cabecera kid, jku y x5u como entradas no fiables: busca kid en tu propia lista de claves y obtén las claves solo del conjunto de claves configurado para el emisor o de direcciones que hayas autorizado de antemano.

En la práctica, estas comprobaciones deben quedar en manos de una biblioteca JWT mantenida y configurada con el emisor, la audiencia y los algoritmos esperados, no de código escrito a mano.

Los ataques clásicos

La mayoría de las vulnerabilidades de los JWT no son debilidades de la criptografía, sino comprobaciones que un servidor se saltó. El RFC 8725 describe estas:

Las comprobaciones de la sección anterior neutralizan cada uno de ellos: una lista fija de algoritmos, con cada clave vinculada a un único algoritmo, detiene los dos primeros; un secreto aleatorio de al menos 256 bits, el tercero, y comprobar aud y typ, el último.

La carga útil es pública

Una firma protege contra los cambios, no contra la lectura. Los tokens viajan en las cabeceras de las solicitudes (y a veces, de forma imprudente, en las URL) y acaban en registros, en el almacenamiento del navegador y en cabeceras Referer, y cualquiera que vea uno puede decodificarlo. Las contraseñas, los secretos y los datos personales que el destinatario no necesite no deberían figurar en una carga útil.

Cuando los claims tienen que viajar con el token y seguir siendo confidenciales, el token puede cifrarse como JWE, que tiene cinco partes en lugar de tres. La alternativa que sugiere OWASP suele ser más sencilla: guardar los datos en el servidor y dar al cliente un token de referencia opaco que por sí solo no significa nada.

Caducidad, revocación y almacenamiento

Un JWT firmado es válido hasta exp, esté donde esté: el servidor no necesita consultarlo en ningún sitio, y eso es lo que hace que los JWT sean rápidos de comprobar, y también la razón por la que un token robado sigue funcionando. Los tiempos de caducidad cortos limitan los daños. Para retirar un token antes, el servidor tiene que conservar, después de todo, algo de estado: OWASP describe una lista de denegación indexada por los claims jti e iss; cada entrada puede eliminarse cuando haya pasado el exp del token.

Las aplicaciones que necesitan sesiones más largas combinan un token de acceso de corta duración con un token de actualización que sirve para obtener otros nuevos. Las prácticas recomendadas de seguridad de OAuth 2.0 (RFC 9700, enero de 2025) exigen que los tokens de actualización emitidos a clientes públicos —normalmente aplicaciones de navegador y móviles, que no pueden guardar un secreto— estén restringidos al remitente, es decir, vinculados criptográficamente a ese cliente, o bien se roten: que se sustituyan por uno nuevo cada vez que se usan y el antiguo quede invalidado.

En el navegador, OWASP desaconseja guardar identificadores de sesión en localStorage, porque un solo fallo de cross-site scripting puede leer todo lo que hay allí. JavaScript no puede leer en absoluto una cookie marcada como HttpOnly, aunque además debería ser Secure y SameSite, y un script inyectado en la página aún puede enviar solicitudes que la lleven.

Preguntas frecuentes

¿Un JWT está cifrado?

Normalmente no. La mayoría de los JWT están firmados, y su cabecera y su carga útil solo están codificadas en Base64URL, así que cualquiera que tenga el token puede leerlas. Solo un JWE, con cinco partes, oculta su contenido.

¿Puedo leer un JWT sin la clave?

Sí, salvo que sea un JWE cifrado. En un JWT firmado, la clave hace falta para crear o verificar la firma, no para leer la cabecera y la carga útil.

¿Cuánto tiempo debería ser válido un JWT?

Las normas no fijan una cifra única. El consejo habitual es que sea lo más breve que permita la aplicación, con tokens de actualización para las sesiones más largas; así, un token perdido solo sirve durante poco tiempo.

¿Debo usar JWT para las sesiones de un sitio web?

No necesariamente. Un ID de sesión clásico en una cookie puede revocarse al instante en el servidor; un JWT ahorra una consulta en cada solicitud, pero no puede retirarse antes de que caduque sin conservar estado adicional.

¿Es seguro pegar un token en el decodificador de nTools?

El token se decodifica en tu navegador y no se envía a ningún sitio. Aun así, es preferible usar un token caducado o de prueba: un token en vigor es una credencial.

Anterior en la serie ← Funciones hash: SHA-256, sumas de comprobación y hash de contraseñas

Referencias

  1. RFC 7519 — JSON Web Token (JWT) (May 2015)
  2. RFC 7515 — JSON Web Signature (JWS) (May 2015)
  3. RFC 7516 — JSON Web Encryption (JWE) (May 2015)
  4. RFC 7518 — JSON Web Algorithms (JWA) (May 2015)
  5. RFC 8725 — JSON Web Token Best Current Practices (February 2020)
  6. RFC 9068 — JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens (October 2021)
  7. RFC 9700 — Best Current Practice for OAuth 2.0 Security (January 2025)
  8. RFC 9864 — Fully-Specified Algorithms for JOSE and COSE (October 2025)
  9. OpenID Connect Core 1.0 incorporating errata set 2 (December 2023)
  10. OWASP — JSON Web Token Cheat Sheet
  11. OWASP — HTML5 Security Cheat Sheet: Local Storage

Guías relacionadas