Anatomía de JWT: Cabecera, Payload, Firma

Un JSON Web Token tiene este aspecto:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Tres secciones separadas por puntos. Cada sección es JSON codificado en Base64URL:

SecciónContieneEjemplo
CabeceraTipo de token + algoritmo de firma{"alg":"HS256","typ":"JWT"}
PayloadClaims — datos de usuario + metadatos{"sub":"123","name":"John","iat":1516239022}
FirmaFirma criptográfica de cabecera+payloadSflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

La firma se calcula con: HMACSHA256(base64url(cabecera) + "." + base64url(payload), secreto). Esto significa que cualquiera puede leer la cabecera y el payload — son solo Base64, no están cifrados. Pero no pueden modificarlos sin invalidar la firma (a menos que conozcan el secreto).

⚠️ Malentendido crítico: Los JWTs están firmados, no cifrados. Cualquiera que tenga el token puede decodificarlo y leer el payload. Nunca pongas secretos (contraseñas, claves de API, datos personales) en los claims de JWT. Usa JWE (JSON Web Encryption) si necesitas confidencialidad.

Cómo Decodificar un JWT en Segundos

Tienes tres opciones:

1. Usa una herramienta decodificadora de JWT (la más rápida)

Pega tu token en el Decodificador JWT de iluv.tools. Decodifica al instante la cabecera y el payload, muestra la hora de expiración en formato legible, y resalta si el token ha expirado. Ningún dato sale de tu navegador.

2. Consola del navegador

// Decodificar payload de JWT
const token = "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.xxx";
const payload = JSON.parse(atob(token.split('.')[1]));
console.log(payload);

3. Línea de comandos

echo "eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMifQ.xxx" | cut -d'.' -f2 | base64 -d 2>/dev/null | python -m json.tool

Qué Contiene Realmente el Payload

Los claims de JWT son los pares clave-valor en el payload. Vienen en tres variedades:

ClaimSignificadoEjemplo
iss (issuer)Quién creó el token"auth.mysite.com"
sub (subject)De quién es el token (ID de usuario)"user_12345"
aud (audience)Para quién está destinado el token"api.mysite.com"
exp (expiration)Marca de tiempo Unix de cuándo expira1716931200
iat (issued at)Marca de tiempo Unix de cuándo se creó1716927600
nbf (not before)Token no válido antes de esta marca de tiempo1716927600
jti (JWT ID)Identificador único para este token"a1b2c3d4"

El claim exp es el más importante para depurar. Si tu API devuelve 401 No Autorizado, decodifica el JWT primero. Verifica exp — convierte la marca de tiempo Unix a una fecha legible. Está en el pasado? Token expirado. Es null o falta? El token puede no expirar nunca (común con servidores de autenticación mal configurados).

Problemas Comunes de JWT y Cómo Solucionarlos

1. "JWT expirado" / errores 401

Decodifica el token. Verifica exp. Si está en el pasado: el token expiró y el cliente necesita renovarlo. La mayoría de los servidores de autenticación establecen exp entre 15 y 60 minutos después de iat. Si exp está 50 años en el futuro: el servidor de autenticación está mal configurado (o es un token de prueba).

2. Errores de "Firma inválida"

El token fue modificado después de firmarse, O el servidor está usando un secreto diferente al que lo firmó. Causas comunes: discrepancia de variables de entorno (secreto de desarrollo vs producción), rotación de secretos, o el cliente truncó/modificó accidentalmente el string del token.

3. "Algoritmo no soportado"

Verifica el campo alg de la cabecera. Si dice "none", el token no está firmado — recházalo. El ataque de "algoritmo none" es clásico: algunas bibliotecas JWT aceptaban tokens con {"alg":"none"} como válidos sin verificar la firma. Siempre valida alg contra una lista blanca.

4. Discrepancia de audiencia

Verifica aud en el payload. Si tu API espera "api.mysite.com" pero el token dice "other-service.com", el token fue emitido para un servicio diferente. Esto ocurre en configuraciones de microservicios donde los tokens se enrutan al servicio equivocado.

Seguridad de JWT: Qué Puede Salir Mal

  • Fuga de tokens — Los JWT son tokens de portador. Cualquiera que tenga el token puede usarlo. Almacena en cookies httpOnly, no en localStorage (XSS puede leer localStorage). Establece tiempos de expiración cortos y usa tokens de actualización para longevidad.
  • Sin mecanismo de revocación — Los JWT son sin estado por diseño. Una vez emitidos, son válidos hasta que expiran. No hay una forma integrada de "cerrar sesión" o "revocar este token". Soluciones: listas negras de tokens (con estado, contradice el propósito), tokens de acceso de corta duración (5-15 min) + tokens de actualización que pueden revocarse.
  • Secretos de firma débiles — Si usas HS256 (HMAC), el secreto debe ser una cadena criptográficamente aleatoria de al menos 256 bits (32 bytes). "misecreto" o "contraseña123" pueden ser forzados por bruta-fuerza en minutos.
  • Ataque de algoritmo none — Siempre valida alg contra una lista blanca. Nunca aceptes "none".
  • Ataque de confusión de claves — Si tu servidor acepta tanto HS256 (simétrico) como RS256 (asimétrico), un atacante puede usar la clave pública RS256 como el secreto HS256 para falsificar tokens. Mitigación: usa rutas de validación separadas, o acepta solo un algoritmo.
💡 Flujo de depuración: Cuando la autenticación falle, decodifica el JWT primero. Responde el 80% de las preguntas al instante: Ha expirado? El ID de usuario es correcto? Los alcances/roles son los esperados? Un decodificador JWT es la primera herramienta, no la última.