📋 Contenidos
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ón | Contiene | Ejemplo |
|---|---|---|
| Cabecera | Tipo de token + algoritmo de firma | {"alg":"HS256","typ":"JWT"} |
| Payload | Claims — datos de usuario + metadatos | {"sub":"123","name":"John","iat":1516239022} |
| Firma | Firma criptográfica de cabecera+payload | SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_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).
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:
| Claim | Significado | Ejemplo |
|---|---|---|
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 expira | 1716931200 |
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 tiempo | 1716927600 |
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
algcontra 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.