Qué es JWT y Cómo Decodificarlo de Forma Segura
Los JSON Web Tokens aparecen en todas partes en el desarrollo web moderno — cabeceras de autenticación, flujos OAuth, claves de API, gestión de sesiones. Si has trabajado con alguna API que requiere un token Bearer, ya has usado un JWT. Pero la mayoría de los desarrolladores los tratan como blobs opacos y rara vez miran dentro. Entender la estructura de un JWT, qué significa cada parte y cómo decodificar uno sin cometer errores de seguridad toma unos quince minutos. Esta guía cubre todo eso.
La Anatomía de un JWT
Todo JWT es una cadena de tres segmentos codificados en Base64URL separados por puntos:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzEyMyIsImlzcyI6Imh0dHBzOi8vYXV0aC5leGFtcGxlLmNvbSIsImF1ZCI6Imh0dHBzOi8vYXBpLmV4YW1wbGUuY29tIiwiZXhwIjoxNzQ3MDAwMDAwLCJpYXQiOjE3NDY5OTY0MDAsInNjb3BlIjoicmVhZDp1c2VycyJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c El formato es encabezado.payload.firma. Cada segmento codifica información diferente.
El Encabezado
El primer segmento es el encabezado. Decodificado, el ejemplo anterior se convierte en:
{
"alg": "RS256",
"typ": "JWT"
} alg especifica el algoritmo usado para firmar el token — RS256 significa RSA con SHA-256. typ identifica el tipo de token. Estos dos campos son el mínimo; algunos tokens también incluyen kid (ID de clave) para decirle al verificador qué clave pública usar cuando hay múltiples claves en rotación.
El Payload
El segundo segmento es el payload — los datos reales. Decodificado:
{
"sub": "user_123",
"iss": "https://auth.example.com",
"aud": "https://api.example.com",
"exp": 1747000000,
"iat": 1746996400,
"scope": "read:users"
} El payload contiene claims: declaraciones sobre la entidad que representa el token. Algunos claims son estandarizados (llamados claims registrados); otros son específicos de la aplicación (llamados claims privados). El payload está codificado en Base64URL, no cifrado. Cualquiera que tenga la cadena del token puede leer cada claim dentro.
La Firma
El tercer segmento es la firma. Se produce tomando el encabezado y el payload codificados, concatenándolos con un punto, y luego firmando el resultado con la clave secreta o clave privada especificada en alg:
signature = sign(
base64url(header) + "." + base64url(payload),
secretOrPrivateKey
) La firma permite que cualquier parte con la clave pública correspondiente (o clave secreta, para algoritmos simétricos) verifique que el token fue emitido por una fuente de confianza y que ni el encabezado ni el payload fueron modificados después de la emisión. Un solo carácter cambiado en el payload invalida completamente la firma.
Cómo Decodificar un JWT
Decodificar es diferente de verificar. Decodificar simplemente lee los datos dentro de un token. Verificar confirma que el token es auténtico y no ha expirado. Siempre debes verificar en el código de producción; decodificar solo es útil para la depuración.
Paso 1: Dividir el token
Divide la cadena JWT por el carácter .. Obtendrás tres segmentos. Si obtienes menos de tres, el token está malformado.
Paso 2: Decodificar encabezado y payload
Decodifica en Base64URL cada uno de los primeros dos segmentos. Base64URL es como el Base64 estándar pero usa - en lugar de + y _ en lugar de /, sin caracteres de relleno. Una vez decodificado, puedes analizar cada segmento como JSON.
Paso 3: Analizar e inspeccionar
Lee los claims. Comprueba exp (expiración) contra el timestamp Unix actual. Mira iss y aud para confirmar que el token está destinado a tu aplicación. No confíes en ningún claim hasta que hayas verificado la firma.
Para inspección rápida durante el desarrollo, usa el Decodificador JWT. Se ejecuta completamente en tu navegador — tu token nunca abandona tu dispositivo. También puedes decodificar segmentos base64 manualmente con el codificador/decodificador Base64 si quieres ver los bytes brutos. Para verificar firmas en scripts de prueba, el generador HMAC te permite reproducir firmas HS256 sin escribir código.
Paso 4: Verificar la firma en el código
En producción, verifica siempre la firma usando una biblioteca construida para tu lenguaje. No implementes la verificación de firma a mano. Las opciones populares incluyen jose o jsonwebtoken para Node.js, PyJWT para Python, golang-jwt/jwt para Go y nimbus-jose-jwt para Java.
Pasa siempre el algoritmo explícitamente. Nunca dejes que la biblioteca infiera el algoritmo del encabezado del token.
Claims Estándar (Claims Registrados)
RFC 7519 define un conjunto de nombres de claims registrados. No son obligatorios, pero cuando están presentes deben seguir la semántica estándar.
iss — Emisor
La entidad que creó y firmó el token. Usualmente una URL que identifica el servidor de autorización (por ejemplo, https://auth.example.com). Los verificadores deben comprobar que iss coincide con un valor esperado y rechazar tokens de emisores desconocidos.
sub — Sujeto
El principal que representa el token — típicamente un ID de usuario, nombre de cuenta de servicio o identificador de dispositivo. El valor debe ser único dentro del contexto del emisor. Tu aplicación usa sub para identificar a qué usuario pertenece la solicitud.
aud — Audiencia
El(los) destinatario(s) previsto(s) del token. Si el identificador de tu API es https://api.example.com, los tokens emitidos para otros servicios deben rechazarse. No validar aud permite a los atacantes reutilizar un token obtenido de un servicio en un servicio diferente.
exp — Tiempo de Expiración
Un timestamp Unix después del cual el token no debe aceptarse. Valida siempre exp. Un token sin expiración vive efectivamente para siempre — si es robado, el atacante tiene acceso indefinido.
iat — Emitido En
El timestamp Unix cuando se creó el token. Útil para detectar tokens que son técnicamente no expirados pero sospechosamente antiguos. Puedes usar iat para imponer una edad máxima del token independiente de exp.
nbf — No Antes De
El timestamp Unix antes del cual el token no debe aceptarse. Menos común que exp, pero útil cuando se emiten tokens con antelación — por ejemplo, una tarea programada que no debe comenzar hasta una hora específica.
jti — ID de JWT
Un identificador único para el token. Permite a los emisores prevenir los ataques de repetición de tokens almacenando los valores jti usados y rechazando tokens con IDs duplicados. Necesario cuando necesitas tokens de un solo uso, como los enlaces de restablecimiento de contraseña.
Vulnerabilidades de Seguridad
Las implementaciones de JWT tienen un historial de vulnerabilidades sutiles. Estas son las más importantes que debes entender antes de enviar código que acepte JWTs.
El Ataque "alg: none"
Algunas bibliotecas JWT de los primeros días del estándar aceptaban un token que especificaba "alg": "none" en su encabezado. Un atacante podía tomar cualquier token válido, reemplazar el algoritmo con none, eliminar la firma, y la biblioteca lo aceptaría como completamente válido — no se requiere firma.
La solución: especifica siempre el algoritmo esperado explícitamente cuando llames a tu función de verificación. Trata cualquier token que declare alg: none como inválido. La mayoría de las bibliotecas modernas han abordado esto, pero vale la pena confirmar los valores predeterminados de tu biblioteca antes de pasar a producción.
Confusión de Algoritmo (Degradación de RS256 a HS256)
Algunas bibliotecas que soportaban ambos algoritmos usaban el campo alg en el encabezado del token para decidir cómo verificar. Un atacante podía cambiar el algoritmo a HS256, luego firmar el token usando la clave pública del servidor como secreto HMAC. El servidor, viendo HS256, verificaría contra la clave pública y aceptaría el token falsificado.
La solución es la misma: bloquea el algoritmo en tu código de verificación. Nunca dejes que el encabezado del token influya en qué algoritmo se usa para la verificación.
Aceptar Tokens Expirados
No validar exp es un descuido común — a veces introducido cuando los desarrolladores añaden un período de gracia que crece sin límite, o cuando la ruta de validación del código se omite en una rama de código. Trata un token expirado igual que un token ausente: recházalo con un 401 y requiere que el cliente se vuelva a autenticar o use un token de actualización.
Datos Sensibles en el Payload
El payload está codificado en Base64URL, no cifrado. Cualquiera que intercepte el token puede decodificarlo. No pongas contraseñas, números de tarjeta de crédito, números de seguridad social u otros datos sensibles en el payload. Si necesitas transmitir claims sensibles de forma segura, usa un token JWE (JSON Web Encryption), que cifra el payload. Los JWTs firmados con JWS (el caso común) solo garantizan autenticidad, no confidencialidad.
Falta de Validación de Audiencia
Omitir la validación de aud significa que un token emitido para el Servicio A puede reproducirse en el Servicio B — siempre que ambos servicios compartan la misma clave de firma o confíen en el mismo emisor. En arquitecturas multi-servicio, valida siempre que la audiencia del token coincide con el identificador de tu servicio.
Mejores Prácticas para 2026
Elige RS256 para la Mayoría de las Aplicaciones
RS256 usa una clave privada para firmar y una clave pública para verificar. Solo el servidor de autorización posee la clave privada. Cualquier servicio puede verificar tokens usando la clave pública, que puede publicarse abiertamente (a menudo a través de un endpoint JWKS). Si cualquier servicio individual se ve comprometido, los atacantes obtienen la capacidad de verificar tokens — pero no de forjar nuevos.
HS256 usa un único secreto compartido tanto para firmar como para verificar. Cualquier servicio que pueda verificar tokens también puede crearlos. En una configuración de microservicios, compartir el secreto entre muchos servicios aumenta el radio de daño de una brecha.
Mantén los Tokens de Acceso de Corta Duración
No hay un mecanismo de revocación incorporado para los JWTs — una vez emitido, un token es válido hasta que expire (a menos que implementes una lista de bloqueo, lo que reintroduce el estado del lado del servidor). Los tiempos de expiración cortos (15 minutos a 1 hora) limitan el margen de daño si se roba un token. Combina los tokens de acceso con tokens de actualización de larga duración almacenados en cookies HttpOnly seguras, no en localStorage.
Usa la Rotación de Token de Actualización
Cuando un cliente usa un token de actualización para obtener un nuevo token de acceso, emite un nuevo token de actualización e invalida el antiguo. De esta forma, un token de actualización robado se detecta la próxima vez que el cliente legítimo intenta usar el original: el servidor ve un token de actualización reutilizado y puede revocar toda la sesión. Este patrón se describe en RFC 6819 y es ampliamente soportado por los servidores de autorización modernos.
Publica un Endpoint JWKS
Un endpoint JSON Web Key Set (JWKS) (/.well-known/jwks.json por convención) publica tus claves públicas de firma en un formato estándar. Otros servicios pueden obtener el JWKS y verificar tokens sin recibir claves fuera de banda. Esto también hace que la rotación de claves sea sencilla: añade la nueva clave al JWKS, empieza a firmar con ella, luego elimina la clave antigua una vez que todos los tokens existentes han expirado.
Rota las Claves Periódicamente
Incluso si tu clave privada nunca se ve comprometida, rotar las claves periódicamente limita la ventana durante la cual una clave robada podría usarse para forjar tokens. Usa kid en tus encabezados JWT para referenciar la clave de firma, para que los verificadores puedan seleccionar la clave pública correcta de tu JWKS sin romper los tokens existentes durante una rotación.
Valida Cada Claim
Comprueba iss, aud, exp y nbf en cada solicitud. No omitas ninguno de estos porque parezcan redundantes en tu configuración — las condiciones que los hacen parecer innecesarios hoy son exactamente las condiciones que cambian durante un incidente.
JWT vs. Cookies de Sesión
Los JWTs y las sesiones del lado del servidor resuelven el mismo problema — persistir el estado de autenticación a través del HTTP sin estado — pero con diferentes compensaciones.
Los JWTs son autónomos. El servidor no necesita consultar una base de datos para validar una solicitud. Esto los hace atractivos para sistemas distribuidos y APIs consumidas por clientes móviles o de terceros. La desventaja es que la revocación requiere una lista de bloqueo (que reintroduce el estado del lado del servidor) o tolerar la vida restante de un token comprometido.
Las cookies de sesión son validadas en el servidor. El servidor almacena el estado de la sesión y puede revocar el acceso al instante eliminando la sesión. Las cookies con las marcas HttpOnly y Secure están protegidas del acceso de JavaScript y la interceptación de red. La desventaja es que cada solicitud requiere una búsqueda en la base de datos, lo que puede convertirse en un cuello de botella a escala.
Para las aplicaciones web tradicionales con páginas renderizadas por el servidor y un único backend, las cookies de sesión siguen siendo una opción sólida y más simple. Para las APIs consumidas por múltiples clientes, arquitecturas de microservicios y aplicaciones móviles, los JWTs con expiración corta y rotación de token de actualización son la opción más práctica.
Decodifica tu Token
La forma más rápida de inspeccionar un JWT es pegarlo en el Decodificador JWT. Divide el token, decodifica el encabezado y el payload, y renderiza los claims en un formato legible — todo sin enviar tu token a ningún lugar. Si necesitas codificar o decodificar cadenas Base64URL brutas manualmente, el codificador/decodificador Base64 maneja tanto las variantes estándar como URL-safe. Para verificar firmas HMAC en scripts de prueba, el generador HMAC te permite reproducir una firma HS256 y compararla con lo que contiene tu token.
Para la especificación completa, consulta el RFC 7519 (JWT) y los ejemplos interactivos en jwt.io.