Los tokens JWT aparecen en prácticamente cada aplicación web moderna — encabezados de autenticación, flujos de actualización, control de acceso a APIs. También son uno de los estándares más mal utilizados en el mundo real. Si los estás utilizando, entender qué contiene el token, qué significa decodificar frente a verificar y cuáles de los atajos rompen silenciosamente tu seguridad es imprescindible.
La anatomía de un JWT
Un JWT es tres cadenas base64url codificadas unidas por puntos:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImVtYWlsIjoiYWxpY2VAZXhhbXBsZS5jb20iLCJpYXQiOjE3MTI3NjQ4MDAsImV4cCI6MTcxMjg1MTIwMH0.4Xr8mNkZQWpH2TvL9uY3sKdJwFqBzEcAoMnRiVePxlU
- Encabezamiento — algoritmo y tipo de token (
alg,typ) - Carga útil — las declaraciones (los datos que realmente necesita tu aplicación)
- Firma — firma HMAC o RSA sobre las dos primeras partes
Decodificado, el payload se ve así:
{
"sub": "user_123",
"email": "alice@example.com",
"iat": 1712764800,
"exp": 1712851200
}
Nada de esto está cifrado. Cualquier persona que tenga el token puede leer estos valores. La firma solo prueba que el token no ha sido alterado después de su emisión — no oculta el contenido.
Decodificar no implica verificar
Esta distinción confunde a más desarrolladores de los que debería.
Decodificar divide el token en los puntos y decodifica cada sección mediante base64url. No se necesita un secreto — cualquier herramienta o línea de comando puede hacerlo. Si deseas decodificar un JWT en línea sin instalar nada, pega el token en IO Tools Decodificador JWT y obtén inmediatamente el desglose del encabezado, carga útil y vencimiento.
Verificar comprueba que la firma es válida utilizando el secreto (o la clave pública para algoritmos asimétricos). También confirma que el token no ha expirado y que las declaraciones coinciden con lo que espera tu aplicación. Omitir la verificación y confiar en un token decodificado es cómo ocurren los bypass de autenticación.
Aquí tienes un fragmento en Node.js que verifica un JWT y maneja los casos comunes:
import jwt from 'jsonwebtoken';
const SECRET = process.env.JWT_SECRET;
function verifyToken(token) {
try {
const payload = jwt.verify(token, SECRET, {
algorithms: ['HS256'], // whitelist — never allow 'none'
audience: 'myapp', // validate aud claim
});
// jwt.verify throws if exp is in the past, but be explicit:
if (payload.exp < Math.floor(Date.now() / 1000)) {
throw new Error('Token expired');
}
return payload;
} catch (err) {
// Never silently swallow verification failures
throw new Error(`Invalid token: ${err.message}`);
}
}
Declaraciones que merecen validarse
El especificación JWT define declaraciones estándar que tu servidor debe verificar activamente, no solo leer:
| Claim | Significado | ¿Validar? |
|---|---|---|
exp | Timestamp de vencimiento | Siempre — los tokens obsoletos constituyen una superficie real de ataque |
iat | Timestamp de emisión | Opcional, útil para comprobaciones de max-age |
sub | Asunto (normalmente ID de usuario) | Sí — confirmar que coincida con el usuario esperado |
aud | Intendido para el público | Sí — evita que los tokens de un servicio A se usen en el servicio B |
La mayoría de las bibliotecas JWT las validan exp automáticamente — pero solo si las configuras. Lee las documentaciones de tu biblioteca. No asumas que está activa por defecto.
Tres errores que dejan a los desarrolladores en problemas
1. El alg: none Ataque
La especificación JWT permite un valor de algoritmo de none, lo que significa que no hay firma. Algunas bibliotecas — especialmente las más antiguas — aceptan esto y omiten completamente la verificación de firma. Un atacante quita la firma, establece "alg": "none" en el encabezado y forja declaraciones arbitrarias. El servidor las confía.
Solución: blancos explícitamente los algoritmos al verificar. Nunca aceptes none. El fragmento anterior demuestra esto con algorithms: ['HS256'].
2. No validar la expiración
Decodificar un token y confiar en la carga útil sin comprobar exp significa que un token emitido hace meses sigue siendo aceptado. Si una sesión de un usuario ha sido revocada o un atacante ha robado un token antiguo, tu aplicación nunca lo sabrá.
Solución: trate la expiración como obligatoria, no opcional. Para comprobar cuándo un token específico se agota, IO Tools Verificador de vencimiento JWT decodifica el exp declaración y te dice exactamente cuánto tiempo queda — útil para depurar flujos de actualización sin escribir código.
3. Almacenar JWTs en localStorage
localStorage es legible por cualquier JavaScript en la página. Una sola vulnerabilidad XSS significa que un atacante puede exfiltrar silenciosamente el token de autenticación de tu usuario. Aquí cómo se comparan las opciones de almacenamiento:
| Almacenamiento | Riesgo XSS | Riesgo CSRF | Accesible desde JavaScript | Notas |
|---|---|---|---|---|
| localStorage | Alto | Ninguno | Sí | Evitar para tokens de autenticación |
| sessionStorage | Alto | Ninguno | Sí | Mismos riesgos que localStorage |
| Cookie httpOnly | Ninguno | Medio | No | Mejor para autenticación; combinar con SameSite + token CSRF |
| En memoria (variable JS) | Bajo | Ninguno | Sí (mismo contexto) | Perdida en actualización; adecuado para tokens de corta duración |
Las cookies httpOnly no pueden ser leídas por JavaScript en absoluto, lo que elimina completamente el vector de robo por XSS. La compensación es la exposición a CSRF, que manejas con SameSite=Strict o un token CSRF.
JWTs frente a sesiones: el verdadero equilibrio
Los JWTs son inestables — el servidor los valida sin necesidad de consultar una base de datos. Esto es útil en sistemas distribuidos donde no se quiere que cada servicio acceda a un almacenamiento compartido de sesiones.
Pero la inestabilidad tiene un costo real: no se puede revocar un JWT antes de que expire. Si un usuario se desconecta o es comprometido, el token permanece válido hasta exp. Existen soluciones (listas de bloqueo de tokens, expiración corta + tokens de actualización), pero añaden complejidad y a menudo recrean lo que ya hace un almacenamiento de sesiones.
Usa JWTs cuando: tienes múltiples servicios autenticando solicitudes de forma independiente, deseas incluir roles y permisos directamente en el token, o tus tokens son de corta duración y la latencia de revocación es aceptable.
Usa sesiones cuando: necesitas revocación inmediata (el cierre de sesión debe funcionar realmente), estás construyendo una aplicación renderizada en el servidor, o la simplicidad supera la escalabilidad inestable.
Inspeccionar un token en segundos
¿Necesitas ver qué contiene un JWT ahora mismo? Terminal:
echo "YOUR.JWT.HERE" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool
O salta la terminal — pega el token en IO Tools Decodificador JWT para obtener un desglose formateado del encabezado y la carga útil. Ninguna de estas opciones verifica la firma; solo decodifica. Para una lectura rápida de vencimiento sin escribir código, el Verificador de vencimiento JWT te da el timestamp exacto y el tiempo restante.
Instalar extensiones
Agregue herramientas IO a su navegador favorito para obtener acceso instantáneo y búsquedas más rápidas
恵 ¡El marcador ha llegado!
Marcador es una forma divertida de llevar un registro de tus juegos, todos los datos se almacenan en tu navegador. ¡Próximamente habrá más funciones!
Herramientas clave
Ver todo Los recién llegados
Ver todoActualizar: Nuestro última herramienta se añadió el 16 de abril de 2026
