Decodificar un JWT solo permite leerlo: la firma y el tiempo deciden si se puede confiar en él
Un JWT (JSON Web Token) es una cadena de tres partes separadas por dos puntos: cabecera, payload y firma. Las dos primeras son JSON escrito en Base64url, así que cualquiera puede decodificarlas y leerlas sin ninguna clave. Por eso, ver el contenido tras decodificar no indica ni que el token sea auténtico ni que se pueda usar ahora. Un servidor acepta un token solo después de comprobar, por orden, que la firma es válida, que la fecha de caducidad (exp) no ha pasado y que el emisor (iss) y el destinatario (aud) son los valores esperados.
La herramienta de arriba decodifica al instante el token que pegas y muestra en una tabla los valores y el significado de la cabecera y el payload. Después sigue los mismos cuatro puntos de control (formato, firma, tiempo, emisor y destinatario) y marca dónde se detiene el token. Si añades una clave secreta o una clave pública (PEM, JWK, JWKS), comprueba la firma de verdad, y el tiempo que queda hasta exp baja segundo a segundo en la línea de tiempo. El token y la clave se procesan solo en el navegador y no se envían a ninguna parte.
Cuándo conviene decodificar un JWT a mano
- Cuando una API devuelve 401 Unauthorized: el motivo suele ser la caducidad (exp), un token pensado para otro servicio (aud) o una rotación de claves (kid). Pega el token y verás en cuál de los cuatro puntos de control se detiene.
- Cuando la sesión termina antes de lo esperado: la diferencia entre iat y exp es la duración del token. Los tokens de acceso suelen durar solo 15 minutos o una hora, así que la duración y el tiempo restante que muestra la pantalla sirven para comprobar la configuración.
- Cuando integras el inicio de sesión con OpenID Connect: comprueba que valores como iss, aud, nonce y email_verified del token de ID llegan como esperabas. Si pegas tal cual el JSON del jwks_uri del proveedor, la herramienta comprueba la firma con la clave pública cuyo kid coincide.
- Cuando buscas por qué falta un permiso: reclamaciones como scope, roles y groups se separan en etiquetas para ver si el permiso necesario está presente. El significado de los valores lo define el servicio, así que compáralos con la configuración del servidor.
- Cuando el reloj del servidor y el del dispositivo no coinciden: si justo después de emitirlo aparece "aún no válido" o "caducado", la causa puede ser una diferencia de reloj. Cambia la tolerancia entre 0 s, 60 s y 5 min y observa cómo cambia el veredicto.
- Cuando necesitas un token de prueba: en "Editar JSON", cambia el payload y fírmalo de nuevo con una clave secreta para obtener un token con los valores que quieras. Si editas sin la clave, también verás de inmediato que la firma deja de coincidir.
Cómo se usa
- Añadir el token: pega el JWT en el campo del token. La herramienta encuentra solo el token aunque venga mezclado con otro texto, como una cabecera
Authorization: Bearer …, el JSON de una respuesta de token ({"access_token": "…", "id_token": "…"}) o el#id_token=…de una URL de callback. Si hay varios tokens, elige uno con las etiquetas. - Revisar las partes: el texto del token se colorea por cabecera, payload y firma, y la barra inferior muestra los bytes de cada parte. Al pasar el ratón sobre una fila de la tabla se resaltan los caracteres del token que contienen ese valor.
- Revisar el tiempo: la sección de validez muestra el tiempo que queda hasta la caducidad, iat, nbf y exp convertidos a la zona horaria del dispositivo, y la duración del token. Elige la tolerancia de reloj que corresponda a la biblioteca que usa tu servidor.
- Comprobar la firma: si alg es un algoritmo HS como HS256, introduce la clave secreta. Para RS, PS, ES y EdDSA, introduce la clave pública. Si indicas el emisor y el destinatario que espera tu servidor, también se comprueba el cuarto punto de control.
- Editar: elige "Editar JSON" en "Contenido" para cambiar tú mismo la cabecera y el payload. Si la sección de comprobación de firma tiene una clave secreta o privada, la herramienta crea un token firmado de nuevo con el contenido editado.
Tres partes separadas por dos puntos
La cabecera y el payload son JSON en UTF-8 convertido a Base64url. Base64url usa - y _ en lugar de los + y / del Base64 normal y omite el = final (RFC 7515, apartado 2), por lo que puede ir directamente en una URL o en una cabecera HTTP. La firma son los bytes calculados con una clave sobre todo el texto "cabecera.payload", escritos de la misma forma, así que cambiar un solo carácter de la cabecera o del payload hace que deje de coincidir. A continuación se muestra, dividido en partes, un token HS256 creado con la clave secreta demo-secret-for-roberin-jwt-2026.
| Parte | Texto del token | Decodificado | Caracteres | Bytes |
|---|---|---|---|---|
| Cabecera | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 | {"alg":"HS256","typ":"JWT"} | 36 | 27 |
| Payload | eyJzdWIiOiJ1c2VyLTEwMjQiLCJpYXQiOjE3MDQwNjcyMDAsImV4cCI6MTcwNDA2ODEwMH0 | {"sub":"user-1024","iat":1704067200,"exp":1704068100} | 71 | 53 |
| Firma | 5YZB1gnh7IFicIbrPtT-mJoFb3uuYFcYTk84hPIA7Ro | Valor HMAC-SHA256 (no es JSON) | 43 | 32 |
Cada 3 bytes se convierten en 4 caracteres, así que una parte en Base64url es aproximadamente un tercio más larga que el JSON original. La longitud de la firma depende del algoritmo: HS256 da 32 bytes (43 caracteres), RS256 da el tamaño de la clave RSA (con una clave de 2048 bits son 256 bytes, 342 caracteres) y ES256 da 64 bytes (86 caracteres). Si hay cinco partes, el token es un JWE cifrado (RFC 7516). La cabecera de un JWE se puede leer, pero las otras cuatro partes no se pueden decodificar sin la clave del destinatario.
Reclamaciones habituales y qué comprobar
Cada elemento del payload se llama reclamación (claim). Las siete reclamaciones registradas por RFC 7519 y los nombres definidos por OpenID Connect tienen un significado fijo, y cualquier otro nombre es un valor que define por su cuenta el servicio emisor (Private Claim). La columna "Qué comprobar" de la tabla indica lo que debe hacer el servidor que recibe el token.
| Reclamación | Significado | Qué comprobar |
|---|---|---|
iss | Emisor | Debe ser igual, carácter por carácter, al valor del emisor de confianza. Una / de más al final ya lo convierte en otro valor. |
sub | Sujeto (ID de usuario) | Es único solo dentro del emisor, así que combínalo con iss para distinguir a los usuarios. |
aud | Destinatario | Una cadena o una matriz. Rechaza el token si tu propio nombre no figura en ella (RFC 7519 4.1.3). |
exp | Fecha de caducidad | El token está caducado cuando la hora actual es igual o posterior a exp. Para compensar diferencias de reloj se puede admitir una tolerancia de unos pocos minutos como máximo. |
nbf | No válido antes de | Si la hora actual es anterior a nbf, el token todavía no se debe aceptar. |
iat | Emitido en | Sirve para ver cuánto tiempo tiene el token. Por sí solo no decide la caducidad. |
jti | ID del token | Si registras los jti ya usados, puedes impedir que el mismo token se reutilice. |
nonce | Valor de comprobación de la petición (OIDC) | Debe ser igual al valor enviado en la petición de inicio de sesión. Si es distinto, el token pertenece a otro inicio de sesión. |
azp | App que recibió el token (OIDC) | Si existe, comprueba que es igual a tu propio client_id. |
scope | Ámbito de permisos | Una lista separada por espacios. Comprueba que incluye el ámbito que necesita la acción solicitada. |
Cómo leer exp, nbf e iat, y la tolerancia de reloj
Las tres fechas son segundos contados desde las 00:00 UTC del 1 de enero de 1970 (NumericDate de RFC 7519). Expresado en hora de Madrid, el mismo instante suma 1 hora en invierno y 2 horas en horario de verano. Un valor escrito en milisegundos tiene 13 dígitos y, leído como segundos, apunta a decenas de miles de años en el futuro, por lo que el token prácticamente nunca termina. La herramienta añade una advertencia cuando encuentra un valor así.
| Valor | UTC | Hora de Madrid | Nota |
|---|---|---|---|
1704067200 | 2024-01-01 00:00:00 | 2024-01-01 01:00:00 (UTC+01:00) | iat del token de ejemplo anterior |
1704068100 | 2024-01-01 00:15:00 | 2024-01-01 01:15:00 (UTC+01:00) | exp del token de ejemplo, duración 900 segundos = 15 minutos |
2000000000 | 2033-05-18 03:33:20 | 2033-05-18 05:33:20 (UTC+02:00, horario de verano) | Un valor en segundos de diez dígitos |
1704067200000 | (leído como segundos: año 55969) | - | Escrito por error en milisegundos, hay que dividirlo entre 1000 |
El reloj de tu servidor y el del servidor emisor pueden diferir entre unos segundos y unos minutos. RFC 7519 indica que, al comprobar exp y nbf, se puede admitir una pequeña tolerancia, "normalmente de no más de unos minutos". El valor por defecto real varía según la biblioteca, así que el mismo token puede ser aceptado por un servidor y rechazado por otro.
| Biblioteca | Nombre del ajuste | Valor por defecto |
|---|---|---|
| PyJWT (Python) | leeway | 0 s |
| jsonwebtoken (Node.js) | clockTolerance | 0 s |
| Spring Security (Java) | JwtTimestampValidator clockSkew | 60 s |
| Microsoft.IdentityModel (.NET) | TokenValidationParameters.ClockSkew | 5 min (300 s) |
El veredicto de la herramienta sigue el de jsonwebtoken. Si la hora actual (redondeada hacia abajo al segundo) es igual o posterior a exp más la tolerancia, el token está caducado, y si nbf es posterior a la hora actual más la tolerancia, todavía no es válido. Cuando la fecha de caducidad ya pasó pero sigue dentro de la tolerancia, el punto de control de tiempo se supera y se muestra el margen restante.
Comprobar la firma: la clave que necesita cada algoritmo
El alg de la cabecera es solo el nombre del método con el que el token dice haber sido firmado. El receptor no debe fiarse sin más, así que un servidor tiene que fijar de antemano los algoritmos que permite y rechazar cualquier otro (RFC 8725 3.1). La herramienta indica qué tipo de clave corresponde a alg y, si introduces una clave que no encaja, explica el motivo.
| alg | Clave para comprobar | Formatos admitidos | Regla |
|---|---|---|---|
HS256·HS384·HS512 | La misma clave secreta que usó quien lo creó | Texto literal, valor Base64, JWK (kty oct) | La clave debe tener al menos la longitud del hash (256 bits para HS256, RFC 7518 3.2) |
RS256·RS384·RS512 | Clave pública RSA | Clave pública PEM, PKCS#1, certificado X.509, JWK, JWKS | Al menos 2048 bits (RFC 7518 3.3) |
PS256·PS384·PS512 | Clave pública RSA | Igual que la familia RS | RSA-PSS, longitud de la sal = longitud del hash (RFC 7518 3.5) |
ES256 | Clave pública P-256 | PEM, certificado, JWK (kty EC), JWKS | La firma es R y S concatenados, 64 bytes (RFC 7518 3.4) |
ES384 | Clave pública P-384 | Igual que ES256 | Firma de 96 bytes |
ES512 | Clave pública P-521 | Igual que ES256 | Firma de 132 bytes (el nombre de la curva es P-521) |
EdDSA·Ed25519 | Clave pública Ed25519 | PEM, JWK (kty OKP) | RFC 9864 (octubre de 2025) indica usar el nombre Ed25519 en lugar de EdDSA |
Si introduces una clave privada, la herramienta extrae la clave pública y comprueba con ella, pero para comprobar basta con la clave pública. Al copiar una clave secreta suele colarse un salto de línea al final, así que, si la clave tal como la introdujiste no coincide pero sí al quitar los espacios de los extremos, la herramienta te lo indica. Un token ES cuya firma está en formato DER (empieza por 0x30) lo creó mal otra biblioteca, y la herramienta señala esa causa por separado.
Por qué los puntos de control siguen el orden de un servidor
El apartado 7.2 de RFC 7519 describe el orden para recibir un JWT: separar las partes, decodificar Base64url y JSON, y comprobar la firma. Si falla cualquier paso, hay que rechazar el token. Además, RFC 8725 exige comprobar que la clave de firma pertenece al emisor cuando hay iss (3.8) y, si un mismo emisor sirve a varias aplicaciones, rechazar el token cuyo aud falte o no sea el tuyo (3.9). Los cuatro puntos de control de la herramienta siguen exactamente este orden y escriben en la línea de veredicto el motivo del primer punto en el que se detiene el token. Mientras no se ha comprobado la firma, los demás puntos quedan como "aún no comprobado" aunque se superen.
Si el permiso es el correcto, si la sesión ya cerró y si se volvió a usar el mismo jti no se puede saber solo con el token. El servidor comprueba esas condiciones con sus propios registros y ajustes, así que superar los cuatro puntos de control solo significa que "se cumplen las condiciones del propio token".
Editar el JSON y volver a firmar
Al cambiar la cabecera o el payload en "Editar JSON", la herramienta crea un token nuevo y lo coloca en el campo del token. Si la sección de comprobación de firma tiene una clave secreta HS o una clave privada RS, PS, ES o Ed25519 (PKCS#8, PKCS#1, clave privada EC o un JWK con d), el token se firma de nuevo con esa clave. Si solo hay una clave pública o no hay ninguna clave, se mantiene la firma original sin cambios, así que el punto de control de la firma pasa a no coincidir. El ejemplo "Token modificado" es justo este caso. El valor de role se cambió de user a admin mientras la firma seguía igual, así que al comprobarlo con la clave secreta se descubre enseguida.
Los botones "iat a ahora y exp a 15 min, 1 h o 1 día" cambian de una vez las fechas de un token de prueba. Si existe nbf, también se pone en la hora actual. El JSON editado solo pierde los espacios para quedar en una línea, así que el orden de las claves y la notación de los números se mantienen como los escribiste.
Errores habituales y ataques conocidos
- Un servidor que acepta alg none: cualquiera puede crear un token sin firma. Algunas bibliotecas llegaron a aceptar None y NONE, que solo se diferencian en las mayúsculas, así que hay que bloquearlo con una lista de algoritmos permitidos (RFC 8725 2.1).
- Cambiar un token RS256 por HS256: el atacante cambia alg a HS256 y firma usando la clave pública del servidor como clave secreta HMAC. Usar una clave para un único algoritmo lo impide. La herramienta avisa de esta forma cuando introduces una clave pública para un token HS.
- Una clave secreta fácil de recordar: con un solo token HS256 en la mano, la clave secreta se puede encontrar con un ataque de diccionario. Genera la clave secreta al azar (RFC 8725 3.5) y con una longitud igual o mayor que la del hash: 32 bytes para HS256 (RFC 7518 3.2).
- Un servidor que no comprueba aud: acepta un token que el mismo emisor dio a otro servicio. Usar un token de ID para acceder a una API es un error que hay que bloquear por el mismo motivo.
- Buscar la clave usando kid o jku tal cual: si pones kid directamente en una ruta de archivo o en SQL, o descargas las claves de la dirección jku de la cabecera, la comprobación acaba usando una clave elegida por el atacante (RFC 8725 3.10).
- Datos personales en el payload: un JWT firmado no está cifrado. Si incluyes un correo electrónico o un teléfono, se podrán leer en cualquier registro o navegador por el que pase el token.
- Caracteres añadidos al copiar: si se cuelan Bearer, comillas o saltos de línea, el servidor no puede leer el token. La herramienta quita estos caracteres antes de leerlo e indica qué quitó.
Límites y precauciones
- En un JWE (cinco partes), la herramienta lee solo la cabecera y no descifra. Tampoco lee JWT anidados, tokens con b64 en false (RFC 7797) ni JWS en serialización JSON.
- La comprobación de la firma usa Web Crypto del navegador. ES256K y Ed448 no se pueden comprobar, y en navegadores antiguos que no admiten Ed25519 la herramienta indica que no puede comprobarlo.
- No descarga claves de una dirección jku o x5u ni del jwks_uri de un proveedor. Tienes que pegar tú mismo la clave pública, una decisión tomada para que el token nunca se envíe al exterior.
- El veredicto de tiempo se basa en el reloj de este dispositivo. Si el reloj del dispositivo está mal, el tiempo restante y el veredicto fallan en la misma medida.
- Aunque se superen los cuatro puntos de control, solo el servidor conoce los permisos, el cierre de sesión y la reutilización. Evita pegar claves secretas de producción y tokens de usuarios reales siempre que puedas, y comprueba con claves y tokens de prueba.
Base: las reglas de JWT, JWS, JWE, JWK y JWA proceden de RFC 7519, 7515, 7516, 7517 y 7518, las recomendaciones de seguridad de RFC 8725, el nombre Ed25519 de RFC 9864, las reclamaciones del token de ID de OpenID Connect Core 1.0, y las tolerancias por defecto del código fuente de cada biblioteca, consultado el 22 de octubre de 2026. Se confirmó que los tokens y las fechas de las tablas dan los mismos valores en esta herramienta y en Python PyJWT 2.13.
Fuentes
- 01IETF — RFC 7519: JSON Web Token (JWT)Apartado 2 NumericDate, apartado 4 nombres de reclamación únicos, 4.1 reclamaciones registradas (iss, sub, aud, exp, nbf, iat, jti), 4.1.4 tolerancia de exp, 7.2 orden de validación
- 02IETF — RFC 7515: JSON Web Signature (JWS)Apartado 2 Base64url (sin el = final), apartado 4 nombres de cabecera únicos, 4.1 cabeceras registradas (alg, kid, jku, x5u, typ, cty, crit)
- 03IETF — RFC 7518: JSON Web Algorithms (JWA)3.2 longitud de la clave HMAC, 3.3 y 3.5 RSA de 2048 bits y longitud de la sal PSS, 3.4 firma ECDSA R‖S
- 04IETF — RFC 8725: JSON Web Token Best Current Practices2.1 none y sustitución de algoritmo, 2.2 claves simétricas débiles, 3.1 algoritmos permitidos, 3.8 la clave pertenece al emisor iss, 3.9 comprobación de aud, 3.10 kid, jku y x5u, 3.11 typ
- 05IETF — RFC 7516: JSON Web Encryption (JWE)7.1 Serialización compacta con cinco partes
- 06IETF — RFC 7517: JSON Web Key (JWK)JWK y JWK Set (matriz keys), parámetros kid, use y alg
- 07IETF — RFC 9864: Fully-Specified Algorithms for JOSE and COSEOctubre de 2025, registra Ed25519 y Ed448 y marca EdDSA como obsoleto (Deprecated)
- 08OpenID Foundation — OpenID Connect Core 1.0Apartado 2 token de ID (auth_time, nonce, acr, amr, azp), 5.1 reclamaciones estándar (name, email, email_verified y otras)
- 09IETF — RFC 9068: JWT Profile for OAuth 2.0 Access Tokenstyp at+jwt, reclamaciones client_id y scope
- 10PyJWT — jwt/api_jwt.pyValor por defecto de leeway en decode: 0
- 11auth0/node-jsonwebtoken — verify.jsSi falta clockTolerance es 0; caducado cuando ahora ≥ exp + tolerancia
- 12Spring Security — JwtTimestampValidatorDEFAULT_MAX_CLOCK_SKEW de 60 segundos
- 13Microsoft.IdentityModel.Tokens — TokenValidationParametersDefaultClockSkew de 300 segundos (5 minutos)
- 14MDN — SubtleCrypto.verify()La API Web Crypto que comprueba en el navegador firmas HMAC, RSASSA-PKCS1-v1_5, RSA-PSS, ECDSA y Ed25519
Preguntas frecuentes
¿Se puede saber si un JWT es válido solo decodificándolo?
No. La cabecera y el payload están en Base64url y cualquiera puede decodificarlos, así que ver el contenido solo significa que el formato es correcto. Para saber si es válido hay que comprobar también que la firma coincide, que exp no ha pasado y que iss y aud son los valores esperados. Los cuatro puntos de control de la herramienta de arriba siguen exactamente este orden.
¿Está cifrado el payload de un JWT?
Un JWT firmado (dos puntos, tres partes) no está cifrado. La firma solo garantiza que el contenido no cambió y no lo oculta. No incluyas en el payload valores que otros no deban ver, como un correo electrónico o un teléfono, o usa un JWE cifrado.
¿Por qué obtengo un 401 si exp todavía no ha pasado?
El token se rechaza cuando la clave de firma se rotó y no hay una clave pública que coincida con el kid, cuando aud es distinto del nombre de la API, o cuando iss difiere de la configuración del servidor aunque sea en un solo carácter. Si el reloj del servidor va adelantado, un token cercano a su caducidad puede juzgarse caducado antes de tiempo. Si el servidor revocó el token por separado, por ejemplo tras un cierre de sesión, no se puede saber solo con el token.
Introduje la clave secreta HS256 pero obtengo que la firma no coincide.
Primero comprueba si la clave secreta de la configuración del servidor es texto normal o un valor Base64, y alterna entre 'Texto literal' y 'Valor Base64'. La herramienta quita el salto de línea añadido al copiar y vuelve a comprobar. Si sigue sin coincidir, la clave es de otro entorno (desarrollo o producción), o la cabecera o el payload cambiaron después de firmar.
¿Dónde consigo la clave pública para comprobar un token RS256?
Si es un proveedor de OpenID Connect, la lista de claves públicas (JWKS) está en el jwks_uri del documento que hay en la dirección del emisor más /.well-known/openid-configuration. Pega ese JSON tal cual y la herramienta elige la clave con el mismo kid que la cabecera del token y comprueba con ella. También puedes introducir una clave pública PEM o un certificado X.509.
¿Es seguro pegar aquí un token?
El token y la clave se procesan solo en este navegador y nunca se envían ni se guardan. Aun así, un token de acceso de producción es una llave que funciona hasta que caduca, así que ten cuidado al compartir o capturar la pantalla y, si puedes, comprueba con un token de prueba.
¿En qué unidad está el número de exp?
En segundos contados desde las 00:00 UTC del 1 de enero de 1970. 1704067200 es la 00:00 UTC del 1 de enero de 2024, que en hora de Madrid es la 01:00 de ese mismo día. Un valor de 13 dígitos se escribió por error en milisegundos y, leído como segundos, apunta a decenas de miles de años en el futuro, así que hay que dividirlo entre 1000.
¿Qué es un token con cuatro puntos (cinco partes)?
Es un JWE cifrado. Solo se puede leer la primera parte, la cabecera, y el resto hay que descifrarlo con la clave del destinatario para ver el contenido. Esta herramienta muestra solo el alg y el enc de la cabecera del JWE y el número de bytes de cada parte.