JWT (JSON Web Tokens): Autenticación Segura en JavaScript
Aprende qué es JWT, cómo funciona, y las mejores prácticas para implementar autenticación segura con JSON Web Tokens en tus aplicaciones JavaScript.
TL;DR - Resumen rápido
- JWT es un estándar para transmitir información de forma segura entre partes como un objeto JSON
- Los tokens JWT consisten en tres partes separadas por puntos: header, payload y signature
- La firma digital garantiza que el token no ha sido modificado y proviene de una fuente confiable
- Nunca almacenes información sensible en el payload del JWT porque es decodificable
- Usa HTTPS siempre y implementa tiempos de expiración cortos para mayor seguridad
Introducción a JWT
JSON Web Token (JWT) es un estándar abierto (RFC 7519) que define una forma compacta y autónoma de transmitir información de forma segura entre partes como un objeto JSON. JWT se usa ampliamente para autenticación y autorización en aplicaciones web modernas, permitiendo que el servidor envíe un token al cliente que luego se presenta en solicitudes subsiguientes sin necesidad de mantener sesiones en el servidor.
A diferencia de las sesiones tradicionales que almacenan el estado en el servidor, JWT es stateless: el servidor no necesita mantener información sobre el usuario. El token contiene toda la información necesaria para validar la identidad del usuario, lo que escala mejor y funciona bien en arquitecturas distribuidas. Sin embargo, esta misma característica requiere implementar medidas de seguridad adicionales, ya que una vez emitido, un JWT no puede ser revocado fácilmente.
Ventajas de JWT sobre Sesiones Tradicionales
JWT ofrece varias ventajas sobre las sesiones basadas en cookies: escalabilidad horizontal sin necesidad de compartir estado entre servidores, compatibilidad con aplicaciones móviles y SPA (Single Page Applications), y reducción de la carga en el servidor al no consultar una base de datos para cada solicitud.
Cómo Funciona JWT
Un JWT se compone de tres partes separadas por puntos: header, payload y signature. El header contiene metadatos sobre el token como el algoritmo de firma usado. El payload contiene las reclamaciones (claims) que son declaraciones sobre una entidad y metadatos adicionales. La signature es una firma digital que garantiza que el token no ha sido modificado y proviene de una fuente confiable.
- <strong>Header:</strong> Contiene el tipo de token (typ) y el algoritmo de firma usado (alg)
- <strong>Payload:</strong> Contiene las reclamaciones (claims) como el ID del usuario, roles, fecha de expiración
- <strong>Signature:</strong> Firma digital creada usando el header, payload y una clave secreta
Este ejemplo muestra la estructura de un JWT y cómo se compone de las tres partes. El header especifica que es un JWT y usa el algoritmo HS256. El payload contiene información sobre el usuario y el tiempo de expiración. La signature se crea firmando el header y payload con una clave secreta, garantizando que cualquier modificación del token invalidará la firma.
Advertencia: El Payload es Decodificable
El payload de un JWT es solo codificado en Base64, no encriptado. Cualquier persona con el token puede decodificarlo y leer su contenido. Por lo tanto, NUNCA almacenes información sensible como contraseñas, números de tarjetas de crédito o datos personales en el payload del JWT.
Implementación Básica de JWT
Para implementar JWT en JavaScript, necesitas generar tokens en el servidor y verificarlos en cada solicitud. La generación de tokens requiere una clave secreta que solo debe conocer el servidor. La verificación usa la misma clave para validar que el token no ha sido modificado y no ha expirado. Puedes usar librerías como jsonwebtoken en Node.js para simplificar el proceso.
Generar un Token JWT
Para generar un token JWT, necesitas especificar el payload que contiene la información del usuario y las opciones como el tiempo de expiración. La librería jsonwebtoken en Node.js facilita este proceso con la función sign, que toma el payload, la clave secreta y las opciones, y devuelve el token firmado.
Este código muestra cómo generar un token JWT usando la librería jsonwebtoken. El payload incluye el ID del usuario, su rol y un tiempo de expiración de 1 hora. La función sign crea el token firmando el payload con la clave secreta, que debe mantenerse segura y nunca ser expuesta en el código del cliente.
Verificar un Token JWT
Para verificar un token JWT, usas la función verify de la librería jsonwebtoken. Esta función decodifica el token, verifica la firma usando la clave secreta, y valida que el token no haya expirado. Si la verificación falla, la función lanza un error que debes capturar y manejar apropiadamente.
Este código muestra cómo verificar un token JWT en una ruta protegida. El middleware verifica el token del header Authorization, lo valida usando la clave secreta, y si es válido, añade la información del usuario al objeto request para que las rutas siguientes puedan acceder a ella.
Mejor Práctica: Middleware de Autenticación
Implementa la verificación de JWT como middleware en Express.js. Esto permite proteger múltiples rutas con una sola función y mantiene tu código DRY (Don't Repeat Yourself). El middleware puede manejar errores de autenticación de forma centralizada.
Validación y Verificación de Tokens
La validación de tokens JWT es crítica para la seguridad de tu aplicación. Debes verificar no solo la firma del token, sino también su tiempo de expiración, el emisor (issuer), y la audiencia (audience). La expiración del token es especialmente importante porque previene que tokens robados sean usados indefinidamente después de que el usuario cierre sesión.
Tiempo de Expiración del Token
El tiempo de expiración (exp claim) determina cuánto tiempo es válido un token. Debes establecer tiempos de expiración cortos para reducir el riesgo de tokens robados. Para aplicaciones web, tiempos de 15-30 minutos son comunes. Para aplicaciones móviles, puedes usar tiempos más largos como 24 horas, pero debes implementar refresh tokens para mantener la sesión activa.
Este código muestra cómo configurar diferentes tiempos de expiración según el tipo de aplicación. Los tiempos cortos reducen el riesgo de tokens robados, mientras que los refresh tokens permiten mantener la sesión activa sin comprometer la seguridad.
Refresh Tokens
Los refresh tokens son tokens de larga duración que se usan para obtener nuevos access tokens cuando estos expiran. El access token tiene un tiempo de expiración corto (15-30 minutos) y se usa para autenticar solicitudes. El refresh token tiene un tiempo de expiración más largo (días o semanas) y se almacena de forma segura, solo se usa para obtener nuevos access tokens.
Este código muestra cómo implementar refresh tokens. Cuando el access token expira, el cliente envía el refresh token al endpoint /refresh, que verifica el refresh token y genera un nuevo access token si es válido. Esto permite mantener la sesión activa sin comprometer la seguridad con access tokens de larga duración.
Almacenamiento Seguro de Refresh Tokens
Los refresh tokens deben almacenarse de forma más segura que los access tokens. Usa cookies HttpOnly y Secure para refresh tokens en aplicaciones web, y usa el almacenamiento seguro del sistema operativo (Keychain en iOS, Keystore en Android) para aplicaciones móviles.
Mejores Prácticas de Seguridad
Implementar JWT de forma segura requiere seguir varias mejores prácticas que van más allá de la generación y verificación básica de tokens. Debes considerar cómo se transmiten los tokens, cómo se almacenan, qué información contiene el payload, y cómo manejas la revocación de tokens. Estas prácticas adicionales reducen significativamente el riesgo de vulnerabilidades en tu implementación de JWT.
- <strong>Usa siempre HTTPS:</strong> Los tokens deben transmitirse siempre sobre conexiones HTTPS para evitar interceptación
- <strong>Tiempos de expiración cortos:</strong> Establece expiración de 15-30 minutos para access tokens
- <strong>Nunca almacenes datos sensibles:</strong> El payload es decodificable, no incluyas contraseñas o datos personales
- <strong>Valida todos los claims:</strong> Verifica issuer, audience, expiración y otros claims relevantes
- <strong>Implementa refresh tokens:</strong> Usa refresh tokens de larga duración para mantener la sesión activa
Este código muestra una implementación segura de JWT con múltiples capas de protección. Valida el issuer y audience del token, verifica la expiración, y usa refresh tokens para mantener la sesión activa sin comprometer la seguridad.
Protección Contra Ataques Comunes
Implementa protección contra ataques comunes como replay attacks usando un identificador único (jti claim) en cada token, contra timing attacks usando comparaciones constantes de tiempo, y contra token theft usando cookies HttpOnly y Secure para almacenar tokens en aplicaciones web.
Errores Comunes en JWT
Al implementar JWT, los desarrolladores cometen errores que pueden comprometer la seguridad de la aplicación. Conocer estos errores comunes te ayudará a evitarlos y a implementar una autenticación JWT robusta y segura.
Error 1: Usar el Algoritmo none
Un error grave es aceptar tokens con el algoritmo none en el header. Esto permite que un atacante cree tokens falsificados sin necesidad de la clave secreta, ya que el algoritmo none indica que el token no está firmado. Siempre debes verificar que el algoritmo usado en el token sea uno esperado y rechazar tokens con algoritmo none.
Este código muestra el error de no validar el algoritmo del token. Un atacante puede crear un token con algoritmo none y cualquier payload, y tu aplicación lo aceptará como válido. La solución es verificar el algoritmo en el header y rechazar tokens con algoritmo none.
Error 2: Exponer la Clave Secreta
Otro error común es exponer la clave secreta usada para firmar tokens en el código del cliente o en variables de entorno que no están protegidas. Si un atacante obtiene la clave secreta, puede firmar tokens arbitrarios y acceder a tu aplicación como cualquier usuario. La clave secreta debe mantenerse en el servidor y nunca ser expuesta en el código del cliente.
Este código muestra el error de exponer la clave secreta en el código del cliente. Un atacante puede inspeccionar el código del cliente, obtener la clave secreta, y generar tokens arbitrarios para acceder a tu aplicación. La solución es mantener la clave secreta en el servidor y nunca exponerla en el cliente.
Error 3: No Implementar Expiración
No implementar expiración de tokens es un error grave que permite que tokens robados sean usados indefinidamente. Un atacante que obtiene un token puede usarlo para acceder a la aplicación permanentemente, incluso después de que el usuario legítimo cierre sesión. Debes siempre implementar expiración de tokens y tiempos cortos para reducir el riesgo de tokens robados.
Este código muestra el error de no implementar expiración de tokens. Un token generado sin expiración es válido indefinidamente, lo que permite que un atacante use un token robado permanentemente. La solución es siempre incluir el claim exp con un tiempo de expiración corto.
Resumen: JWT (JSON Web Tokens)
Conceptos principales:
- •JWT es un estándar para transmitir información de forma segura como un objeto JSON
- •Los tokens JWT consisten en tres partes: header, payload y signature
- •La firma digital garantiza que el token no ha sido modificado y proviene de una fuente confiable
- •El payload es decodificable, nunca almacenes información sensible en el token
- •Los tiempos de expiración cortos reducen el riesgo de tokens robados
Mejores prácticas:
- •Usa siempre HTTPS para transmitir tokens y evitar interceptación
- •Implementa tiempos de expiración cortos (15-30 minutos) para access tokens
- •Nunca almacenes información sensible en el payload del JWT
- •Valida todos los claims relevantes: issuer, audience, expiración
- •Implementa refresh tokens para mantener la sesión activa sin comprometer la seguridad