pulsa ⌘K para cambiar de herramienta
SECURITY

Decodificador de Tokens JWT

Decodifica JSON Web Tokens y visualiza su contenido.

local
jwt-decoder

Decoding only — the signature is not verified. Nothing is sent anywhere.

§01 ACERCA DE ESTA HERRAMIENTA

Descripción general

Un JWT son tres partes codificadas en base64url y separadas por puntos: una cabecera que dice cómo se firmó, un payload de claims y una firma sobre las dos primeras. La codificación no es cifrado. Cualquiera que tenga el token puede leer todos sus claims, algo que conviene interiorizar antes de meter cualquier cosa en un payload.

Esta herramienta decodifica las dos primeras partes y las formatea, y después extrae los tres claims de tiempo y los convierte de segundos Unix a fechas que puedas leer. No verifica la firma; más abajo está el motivo por el que eso es la decisión correcta y no una función que falta.

Cómo se usa

  1. Pega el token. Todo lo que va después de Bearer : el esquema en sí no forma parte del JWT.
  2. Lee la cabecera para ver el algoritmo y, si está, el identificador de clave.
  3. Lee el payload para ver los claims.
  4. Revisa la tabla de fechas con iat, nbf, exp y el veredicto de caducidad.

Los claims registrados

ClaimNombreNotas
issEmisorQuién acuñó el token. Tu servidor debería comprobarlo contra una lista de permitidos
subSujetoDe quién habla el token. Normalmente un id de usuario estable, no un correo
audAudienciaPara quién es el token. Un token acuñado para otra audiencia hay que rechazarlo
expCaducidadSegundos Unix. A partir de ahí, rechazar
nbfNo antes deSegundos Unix. Antes de ahí, rechazar
iatEmitido enSegundos Unix. Útil para «reautenticar si es más antiguo que N»
jtiId del JWTId único, para poder revocar un token o comprobar reenvíos

Los siete son opcionales en la especificación. Un token que omite exp no caduca nunca por sí solo, y esa es una decisión de diseño que vale la pena detectar cuando te la encuentras.

Por qué no se ofrece verificación de firma

Verificar una firma necesita la clave: el secreto compartido en HS256, o la clave pública en RS256 y ES256. En el caso simétrico, la clave que verifica es también la clave que firma, así que una página que ofreciera verificación te estaría pidiendo que pegues la credencial que permite a cualquiera acuñar tokens para tu sistema. No existe ninguna versión de eso que sea segura de construir.

Decodificar sigue mereciendo la pena. La mayor parte de la depuración de JWT consiste en «qué dice realmente este token»: audiencia equivocada, un scope que falta, un exp cinco minutos en el pasado, un sub que es un correo cuando tu código espera un UUID. Todo eso se ve sin la clave.

Lo que decodificar no te puede decir es si el token es auténtico. Un JWT que has recibido es una afirmación, no un hecho, hasta que tu servidor comprueba la firma contra una clave en la que confía.

El campo alg no es una decisión que tu verificador deba delegar

El alg de la cabecera describe lo que usó el emisor. Un verificador que lee alg y elige su algoritmo en consecuencia está confiando en una entrada controlada por el atacante, y de ahí salen dos roturas clásicas:

  • alg: none. El JWT no asegurado es legal en la especificación. Un verificador que lo honra acepta cualquier payload con una firma vacía.
  • RS256 cambiado por HS256. Si una biblioteca toma la clave pública y la usa como secreto HMAC, un atacante que conozca tu clave pública —es pública— puede firmar tokens que tú aceptarás.

Las dos son problemas de configuración, no de criptografía. Fija el algoritmo esperado en tu verificador y rechaza cualquier otro, en lugar de leerlo del token.

Ejemplos

  • Un 401 que no sabes explicar. Decodifica y mira aud. Un token acuñado para una API y presentado a otra es la causa más habitual, y el mensaje de error casi nunca lo dice.
  • Un 401 intermitente. Compara exp con iat. Una vida corta más un cliente que cachea el token es intermitente por construcción.
  • Un permiso que debería funcionar. Busca scope o roles en el payload. Si el claim no está, el problema está en el emisor y no en tu código de autorización.
  • Auditar lo que metes en un token. Decodifica uno propio. Todo lo que haya ahí —dirección de correo, ids internos, feature flags— lo puede leer quien tenga el token, incluido el navegador donde está guardado.

Dónde debería vivir un token en el navegador

Esto sale cada vez que alguien decodifica un token y se da cuenta de que es legible. La versión corta: como un JWT es una credencial al portador, cualquier cosa que pueda leerlo puede usarlo.

localStorage lo puede leer cualquier script del origen, lo que significa que un XSS se lleva el token con él. Tampoco se envía nunca de forma automática, así que por esa vía no te pueden hacer CSRF: cambias una clase de fallo por otra.

Una cookie HttpOnly no la puede leer ningún script, así que un XSS no la puede robar. Sí se envía automáticamente, así que necesita SameSite=Lax o Strict más Secure, y cualquier endpoint que cambie estado necesita su propia defensa contra CSRF.

La conclusión habitual para una aplicación de navegador es un token de acceso de vida corta guardado solo en memoria, renovado desde una cookie de refresco HttpOnly, Secure y con SameSite. Así un XSS se lleva un token con minutos de vida en lugar de una credencial que dura semanas, y el secreto de vida larga nunca es alcanzable desde JavaScript.

Elijas lo que elijas, el trabajo de verdad lo hace exp. Un token con una vida de una hora y sin lista de revocación es válido durante una hora después de ser robado, y nada de lo que hagas en el cliente cambia eso.

Notas

Se añade el relleno antes de decodificar, porque base64url normalmente lo omite. Los tokens copiados de los registros a veces llegan con el relleno intacto, y aquí funcionan las dos formas.

El veredicto de caducidad usa tu reloj local, así que es una comodidad y no una autoridad. Si necesitas comparar contra un momento concreto, usa el conversor de tiempo Unix sobre el valor exp en crudo.

Los payloads no siempre son JSON en principio —la especificación permite cualquier tipo de contenido— pero en la práctica todos los JWT llevan un objeto JSON, y un payload que no consigue analizarse como JSON se muestra como texto en crudo en lugar de ocultarse.

FAQ
¿Esto verifica la firma?
No, y es deliberado. Verificar requiere la clave de firma, y pegar una clave de firma en una página web es exactamente el error que esta herramienta no debería fomentar. Decodificar te dice qué afirma un token; solo tu servidor puede decirte si esa afirmación es verdad.
¿Se envía mi token a algún sitio?
No. El token se parte por los puntos y se decodifica en base64url dentro de la página. No hay ninguna petición de red. Aun así, da por gastado cualquier token que pegues en cualquier parte: un token al portador en el historial del portapapeles o en la sesión de tu navegador es una credencial que ya no controlas del todo.
¿Por qué dice EXPIRED si mi API todavía acepta el token?
El estado compara exp con el reloj de tu ordenador. Si tu reloj va desviado, o si el emisor tolera cierto desfase de reloj (unos minutos es lo habitual), los dos veredictos pueden discrepar. El valor exp del propio token es el número que manda; la etiqueta es una comodidad.
¿Puedo decodificar un token que solo tiene dos partes?
Sí. Dos partes es un JWT no asegurado válido (alg: none) o un token que has pegado sin su firma. La herramienta necesita la cabecera y el payload; la firma no se usa.
El payload parece basura. ¿Qué ha pasado?
Lo más frecuente es que el token esté cifrado en lugar de firmado (un JWE, que tiene cinco partes) o que algo lo haya recodificado: copiarlo de una terminal que partió la línea, o de una cadena JSON que lo escapó. El payload de un JWS es base64url puro y siempre decodifica a JSON.