Cómo decodificar e inspeccionar tokens JWT

· 9 min de lectura

Los JSON Web Tokens (JWT) son la manera más común de manejar la autenticación en aplicaciones web modernas. Cuando algo va mal con la autenticación, un usuario se desconecta inesperadamente, los permisos están equivocados, una API devuelve 401, decodificar el JWT suele ser el primer paso de depuración. Entender las tres partes de un JWT, las reclamaciones estándar, los algoritmos con los que puede firmarse, y los errores comunes convierte la depuración de auth de vudú en una comprobación de rutina.

Breve historia del JWT

Los JWT se estandarizaron en la RFC 7519 en mayo de 2015, tras varios años de iteración de borrador en la IETF. El formato tomó prestado de diseños de tokens compactos anteriores (afirmaciones SAML, cookies opacas simples) pero añadió dos cosas que les faltaban: una forma JSON estricta que era legible en cualquier lenguaje, y una codificación base64url-safe que sobrevivía a parámetros de URL, cabeceras HTTP y campos de formulario sin re-escape. Las especificaciones complementarias, JWS (RFC 7515) para firmas, JWE (RFC 7516) para cifrado, y JWA (RFC 7518) para nombres de algoritmo, forman juntas la familia JOSE (JavaScript Object Signing and Encryption).

OAuth 2.0 y OpenID Connect adoptaron JWT como su formato de token por defecto poco después, por lo que casi todos los proveedores de autenticación modernos (Auth0, Okta, Cognito, Keycloak, Firebase, Supabase, Clerk) los emiten hoy. La combinación de tokens autocontenidos y backends sin estado resultó ser un encaje muy natural para microservicios y pasarelas de API. La desventaja es que los JWT son notoriamente fáciles de usar mal, y la última década ha producido un flujo constante de CVE en bibliotecas que no validaron el algoritmo cuidadosamente.

Qué hay dentro de un JWT

Un JWT tiene tres partes, separadas por puntos:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.dozjgNryP4J3jVmNHl0w5N_XgL0n3I9PlFUP0THsR8U

Cabecera: contiene el algoritmo (HS256, RS256, etc.) y tipo de token.

{"alg": "HS256", "typ": "JWT"}

Payload: contiene claims (afirmaciones de datos) sobre el usuario y el token.

{"sub": "1234567890", "name": "Alice", "exp": 1700000000}

Firma, un hash criptográfico que verifica que el token no ha sido manipulado. No puedes leer esto sin la clave de firma.

Cada sección está codificada en base64url, lo que significa que usa - y _ en lugar de + y / y omite el relleno = final. Base64url no es cifrado; pegar solo el segmento del medio en cualquier decodificador revela el payload. Es por diseño: los segmentos del medio están diseñados para ser legibles por los servicios en el camino, la firma es la única parte que prueba autenticidad.

Claims comunes de JWT

Los claims estándar están registrados con IANA y definidos en la RFC 7519. La mayoría son opcionales, pero los de abajo están casi siempre presentes.

ClaimNombre completoQué contiene
subSubject (sujeto)ID de usuario o identificador
expExpiration (expiración)Marca de tiempo Unix cuando expira el token
iatIssued At (emitido en)Marca de tiempo Unix de creación del token
issIssuer (emisor)Quién creó el token (tu servidor de auth)
audAudience (audiencia)A quién va dirigido el token
nbfNot Before (no antes)El token no es válido antes de este momento
jtiJWT IDIdentificador único del token
azpAuthorized PartyLa parte a la que se emitió el token (OIDC)
scope / scpÁmbitos OAuthPermisos concedidos, a menudo separados por espacio
emailCorreoIdentificador estándar OIDC de usuario
nameNombreNombre de visualización (OIDC)
nonceNonceValor OIDC de protección contra reenvío
kid (cabecera)Key IDQué clave de firma se usó (para consulta JWKS)

Más allá del conjunto estándar, las aplicaciones añaden sus propios claims personalizados (roles, tenant_id, feature_flags, permissions). Los nombres de claims personalizados no tienen espacio de nombres por defecto, lo que significa que dos servicios distintos pueden usar el mismo nombre para significar cosas distintas; la convención OIDC de prefijar con un URI (https://myapp.com/roles) evita la colisión.

Cómo decodificar un JWT

  1. Pega tu token: introduce el JWT completo (formato header.payload.signature) en el decodificador. Los decodificadores basados en navegador lo procesan localmente, el token nunca sale de la página.
  2. Mira las secciones decodificadas: la herramienta muestra la cabecera (algoritmo), payload (claims), y firma como JSON formateado, con marcas de tiempo mostradas tanto como enteros Unix como fechas legibles.
  3. Comprueba los claims: examina el tiempo de expiración, emisor, sujeto, audiencia, y cualquier claim personalizado que conduzca tu lógica de autorización.
  4. Compara con expectativas: cruza el emisor con el proveedor de auth que configuraste, la audiencia con la API a la que se envía el token, y cualquier claim de rol/ámbito con los permisos que el usuario debería tener.
  5. Prueba de viaje en el tiempo: pasa el ratón sobre iat, nbf, y exp para ver si el token es válido actualmente, va a expirar pronto, o se emitió hace tanto tiempo que tu tolerancia de desfase de reloj ya no lo cubre.

Algoritmos de firma

No todos los JWT usan la misma cripto. La cabecera alg te dice a qué familia pertenece la firma, y cada una tiene propiedades de seguridad muy distintas.

AlgoritmoFamiliaTipo de claveCuándo elegir
HS256HMACSecreto compartidoApps de un solo servicio; nunca compartas el secreto entre equipos
HS384 / HS512HMACSecreto compartidoComo HS256 con digests más largos
RS256RSAPar de claves pública/privadaLo más común para OIDC; los verificadores solo necesitan la clave pública
RS384 / RS512RSAPar de clavesComo RS256 con claves más grandes
PS256 / PS384 / PS512RSA-PSSPar de clavesRSA moderno, recomendado sobre RS para despliegues nuevos
ES256 / ES384 / ES512ECDSAPar de claves de curva elípticaClaves más pequeñas que RSA, verificación más rápida
EdDSAEd25519Par de claves curva de EdwardsEl más nuevo, pequeño, rápido; aún no universal
noneNingunoNingunaProhibido en producción; algunas bibliotecas antiguas todavía lo aceptan

Los algoritmos asimétricos (RS*, PS*, ES*, EdDSA) permiten a cualquier servicio verificar un token solo con una clave pública, por lo que dominan OIDC. Simétrico (HS*) está bien dentro de una sola aplicación pero se vuelve una pesadilla para rotar o distribuir entre múltiples consumidores.

Depuración con JWT

¿Token expirado? Comprueba el claim exp. Convierte la marca de tiempo Unix en una fecha legible. Si está en el pasado, el token ha expirado y necesita refrescarse. La mayoría de bibliotecas JWT rechazan tokens expirados por defecto; si tu app los acepta, es un bug de seguridad.

¿Permisos equivocados? Busca claims de rol o ámbito en el payload. Varían por implementación pero suelen parecer "role": "admin" o "scope": "read write profile".

¿Problemas de identidad de usuario? El claim sub identifica al usuario. Verifica que coincida con el ID de usuario esperado. Nota que algunos proveedores usan GUIDs opacos mientras otros usan direcciones de correo; el decodificador te muestra exactamente lo que hay.

¿Token no aceptado? Comprueba el claim aud (audiencia). Si la API espera un valor de audiencia específico y el token tiene uno distinto, se rechazará. Los desajustes de audiencia son un síntoma común de enrutar un token al servicio equivocado.

¿Errores 401 tras desplegar? Comprueba el claim iss (emisor). Un nuevo tenant de proveedor de auth o una clave de firma cambiada modifica la URL del emisor; si tu verificador aún confía en la vieja, todos los tokens parecen inválidos.

¿Problemas de desfase de reloj? Si iat está ligeramente en el futuro o exp ligeramente en el pasado, el reloj de tu servidor puede estar derivando. La mayoría de bibliotecas JWT permiten unos segundos de margen; si no, un reloj sincronizado por NTP arregla el problema.

Errores comunes

Alternativas a JWT

JWT es dominante pero no la única opción. Cada alternativa intercambia propiedades distintas.

MecanismoFortalezaDebilidad
JWT (JWS)Autocontenido, fácil entre serviciosNo se puede revocar sin estado extra
Tokens opacos + introspecciónFácil de revocar, oculta claimsCada petición toca el servidor de auth
Sesiones del lado del servidorModelo más simple, revocación instantáneaDifícil escalar entre servicios
PASETOReemplazo más seguro de JWT (sin confusión alg)Ecosistema más pequeño
MacaroonsAtenuación incorporada (derechos delegados)Soporte de bibliotecas limitado
OAuth 2.0 + tokens de acceso JWTEstándar de la industria para APIsEspecificación grande, fácil de mal implementar
Tokens ID OIDCIdentidad de usuario estándar + JWTA menudo confundidos con tokens de acceso
Certificados de cliente mTLSLa auth más fuerte en la capa de transporteSobrecarga de gestión de certificados

Para la mayoría de equipos, la elección está entre JWT y tokens opacos. JWT gana cuando la verificación necesita ser barata y offline; los tokens opacos ganan cuando la revocación tiene que ser instantánea.

Privacidad y el decodificador

El decodificador JWT corre enteramente en tu navegador. El token que pegas se divide, se decodifica en base64url, y el JSON se parsea y se imprime con formato sin ninguna petición de red. No hay registro de los tokens que se han decodificado, ni analítica sobre los claims que contienen, ni manera de que nadie reconstruya para quién estabas depurando. Los JWT contienen a menudo identificadores de usuario, direcciones de correo, nombres de roles internos, e IDs de tenant, exactamente el tipo de metadatos que no quieres enviar al servidor de un extraño. Decodificar del lado del cliente mantiene esa información en tu máquina, que es el valor por defecto correcto para cualquier tarea de depuración que toque autenticación.

Preguntas frecuentes

¿Puedo verificar una firma JWT con un decodificador?

No. La verificación de firma requiere el secreto de firma o la clave pública, que se guardan en tu servidor. Un decodificador te muestra lo que hay en el token, pero la verificación criptográfica debe hacerse en tu backend. Nunca confíes en un JWT no verificado en producción.

¿Es seguro pegar un JWT en una herramienta en línea?

Sí, cuando la herramienta se ejecuta en tu navegador. Los decodificadores en el navegador procesan el token localmente, nada se envía a un servidor. Evita las herramientas que hacen peticiones de red con tu token.

¿Qué es la reclamación exp?

La reclamación exp (expiration) es una marca de tiempo Unix que indica cuándo expira el token. Tras esa fecha, el token debe rechazarse. Comprueba siempre esta reclamación para depurar problemas de autenticación.

¿Pueden cifrarse los JWT?

Los JWT estándar (JWS) están firmados pero no cifrados, cualquiera puede decodificar la carga útil. Los tokens JWE (JSON Web Encryption) están cifrados, pero son menos habituales. Nunca pongas datos sensibles (contraseñas, secretos) en una carga útil JWT estándar.

What is the alg none vulnerability?

Early JWT libraries accepted tokens with an alg header set to "none", meaning the signature could be omitted entirely. An attacker who set this header could forge any payload. Modern libraries reject "none" by default, but legacy systems may still be exposed; always allow-list the expected algorithm rather than trusting the header.

How should I store a JWT on the client?

HttpOnly secure cookies with SameSite=Lax (or Strict) are the safest default; they cannot be read by JavaScript, which mitigates XSS token theft. localStorage is convenient but vulnerable to any XSS bug. Never store long-lived JWTs alongside untrusted scripts.