Si has construido o consumido una API REST moderna, casi con certeza has encontrado un JWT. Los JSON Web Tokens aparecen en encabezados de autorización, parámetros URL y almacenamiento local en prácticamente todos los sistemas de autenticación construidos en la última década. Parecen cadenas aleatorias, pero en realidad son datos estructurados y verificables con un formato preciso que hace posible la autenticación sin estado.
La Estructura de Tres Partes de un JWT
Un JWT consiste en tres partes codificadas en Base64URL separadas por puntos: encabezado.carga_útil.firma. Cada parte es independientemente legible — Base64URL es una codificación, no cifrado. El encabezado identifica el tipo de token y el algoritmo de firma. La carga útil contiene las reclamaciones (los datos que te interesan). La firma prueba que el encabezado y la carga útil no han sido manipulados.
El encabezado es un objeto JSON como {"alg": "HS256", "typ": "JWT"}. El campo del algoritmo especifica cómo se creó la firma. Los algoritmos comunes incluyen HS256 (HMAC-SHA256, simétrico — usa un secreto compartido), RS256 (RSA-SHA256, asimétrico — usa clave privada para firmar y pública para verificar) y ES256 (ECDSA-SHA256, asimétrico — firma más pequeña que RSA).
La carga útil contiene reclamaciones — declaraciones sobre el sujeto y metadatos. Las reclamaciones estándar registradas incluyen sub (sujeto, típicamente un ID de usuario), iss (emisor), aud (audiencia), exp (marca de tiempo de expiración), iat (emitido en) y nbf (no antes de). Dado que la carga útil solo está codificada en Base64URL (no cifrada), nunca pongas datos sensibles como contraseñas en una carga útil JWT.
Cómo Funciona la Verificación de Firma
La firma se calcula tomando el encabezado codificado en Base64URL, un punto, la carga útil codificada en Base64URL, y firmando esa cadena con el algoritmo y clave especificados en el encabezado. Para HS256: HMAC-SHA256(base64url(encabezado) + "." + base64url(carga_útil), secreto). El resultado se codifica en Base64URL para formar la firma.
Cuando un servidor recibe un JWT, vuelve a calcular la firma usando el mismo algoritmo y clave, luego la compara con la firma del token. Si coinciden, el token no ha sido modificado. Sin la clave secreta, un atacante no puede falsificar una firma válida para una carga útil modificada.
La reclamación exp es crítica: siempre valida la expiración del token. Un JWT con una firma válida pero una marca de tiempo exp expirada debe rechazarse. Las bibliotecas manejan esto automáticamente cuando las usas correctamente, pero las implementaciones manuales de JWT a menudo olvidan las verificaciones de expiración. Un JWT sin expiración es una credencial permanente.
Autenticación Sin Estado y Sus Compromisos
La autenticación tradicional basada en sesiones almacena el estado de sesión en una base de datos del lado del servidor: el navegador tiene una cookie de ID de sesión, y el servidor busca el ID de sesión en cada solicitud. Los JWTs invierten esto: toda la información necesaria para autenticar al usuario está incrustada en el propio token, por lo que el servidor no necesita consultar la base de datos para verificar la identidad.
La falta de estado permite el escalado horizontal. Cualquier servidor que tenga la clave de firma puede verificar cualquier JWT — sin base de datos de sesiones, sin sesiones pegajosas, sin sincronización de sesiones distribuidas. Por esto los JWTs se volvieron dominantes en arquitecturas de microservicios y funciones serverless.
El compromiso fundamental es la revocación. Con la autenticación basada en sesiones, puedes invalidar una sesión al instante eliminándola de la base de datos. Con los JWTs, no hay un mecanismo estándar de revocación — un token es válido hasta que expira. La mayoría de los sistemas usan tokens de acceso de corta duración (15 minutos) combinados con tokens de actualización de mayor duración almacenados en una base de datos.
Errores Comunes de Seguridad en JWT
El ataque del algoritmo "none": las primeras bibliotecas JWT aceptaban {"alg": "none"} en el encabezado, lo que significaba que no se requería firma. Un atacante podía modificar la carga útil libremente. Siempre verifica el algoritmo explícitamente en tu código — nunca aceptes el algoritmo que el token afirma.
Almacenar JWTs en localStorage los hace accesibles a JavaScript, exponiéndolos a ataques XSS. Una sola vulnerabilidad XSS en cualquier parte de tu sitio permite a un atacante robar cada JWT en localStorage. Almacenar JWTs en cookies HttpOnly los hace inaccesibles a JavaScript (aunque entonces se necesita protección CSRF).
Usar secretos débiles para HS256 permite ataques de fuerza bruta fuera de línea. Un atacante que intercepta un JWT puede intentar encontrar el secreto de firma fuera de línea probando secretos candidatos contra la firma. Los secretos HS256 deben ser criptográficamente aleatorios y de al menos 256 bits (32 bytes) de longitud.
Los JWTs son una herramienta poderosa y adecuada para la autenticación sin estado en sistemas distribuidos, pero vienen con propiedades de seguridad que requieren elecciones de implementación deliberadas. Comprender la estructura de tres partes, cómo funciona la verificación de firmas y los compromisos en torno a la revocación y el almacenamiento te convierte en un consumidor e implementador más informado. Un decodificador JWT te permite inspeccionar cualquier token al instante — examinar reclamaciones, verificar la expiración y verificar el algoritmo — sin escribir una sola línea de código.