Cómo proteger los tokens JWT: Guía de mejores prácticas

Los Tokens Web JSON (JWT) se han convertido en el estándar de la industria para la autenticación sin estado. Permiten que los servidores verifiquen la identidad de un usuario sin almacenar datos de sesión en el propio servidor. Sin embargo, esta naturaleza sin estado introduce riesgos de seguridad específicos si no se implementan correctamente. En esta guía, exploraremos cómo proteger adecuadamente los tokens JWT para proteger los datos del usuario y mantener la integridad de la aplicación.

Entendiendo la estructura del JWT

Antes de sumergirse en las medidas de seguridad, es crucial entender las tres partes de un JWT: el Encabezado (Header), la Carga útil (Payload) y la Firma (Signature).

  1. Encabezado: Contiene el algoritmo utilizado para firmar el token (por ejemplo, HS256, RS256).
  2. Carga útil (Payload): Contiene las afirmaciones (datos sobre el usuario, como ID y rol).
  3. Firma (Signature): Asegura que el token no ha sido manipulado.

Dado que la carga útil está codificada en base64, es legible para cualquiera que intercepte el token. Por eso, medidas de seguridad como HTTPS y la validación de la firma son imprescindibles.

1. Siempre usa HTTPS

El paso más fundamental para asegurar cualquier token es garantizar que se transmita a través de un canal seguro. Si un JWT se envía a través de HTTP, un atacante en la red puede interceptar y leer la carga útil del token, potencialmente extrayendo información sensible del usuario.

BBVA enfatiza que el uso de HTTPS es obligatorio para prevenir ataques de intermediario (Man-in-the-Middle). Esta cifrado garantiza que el token permanezca confidencial desde el momento en que sale del cliente hasta que llega al servidor.

2. Protege tus secretos de firma

La firma es la única parte del JWT que prueba que el token es auténtico. Para generar una firma válida, necesitas una clave secreta (para algoritmos simétricos) o una clave privada (para algoritmos asimétricos).

No codifiques los secretos en el código

Uno de los errores más comunes que cometen los desarrolladores es codificar la clave secreta directamente en el código fuente. Si tu código se envía a un repositorio público (como GitHub), el secreto queda expuesto. H2S Media aconseja en contra de esta práctica, recomendando en su lugar el uso de variables de entorno para almacenar los secretos. Esto mantiene los datos sensibles separados de la lógica de tu aplicación y previene la exposición accidental.

Rota los secretos regularmente

Incluso si tus secretos se almacenan de forma segura, deben rotarse periódicamente. Si un secreto se compromete, un atacante puede falsificar tokens indefinidamente. La rotación regular limita la ventana de oportunidad para un atacante.

3. Elige el mecanismo de almacenamiento adecuado

Dónde almacenes el JWT en el lado del cliente es una decisión de seguridad crítica. Las dos opciones principales son LocalStorage y Cookies HttpOnly.

Los riesgos de LocalStorage

Almacenar JWTs en localStorage los hace accesibles para cualquier JavaScript que se ejecute en la página. Si tu aplicación tiene una vulnerabilidad de Cross-Site Scripting (XSS), un atacante puede fácilmente robar el token e impersonar al usuario. MakeUseOf destaca que si bien localStorage es conveniente, es vulnerable a ataques XSS.

La seguridad de las Cookies HttpOnly

Las cookies HttpOnly no son accesibles a través de JavaScript. Esto las hace inmunes a los ataques XSS que intentan robar el token. Para máxima seguridad, especialmente en aplicaciones que manejan datos sensibles, almacenar el JWT en una cookie HttpOnly es el enfoque recomendado. Este método también ayuda a mitigar los ataques CSRF (Cross-Site Request Forgery) si se combina con los atributos SameSite adecuados.

4. Implementa tiempos de expiración cortos

Los JWTs están diseñados para ser sin estado, lo que significa que son válidos hasta que expiran. Sin embargo, cuanto más tiempo es válido un token, mayor es el riesgo si es interceptado. AWS recomienda usar tiempos de expiración cortos para los tokens de acceso. Al mantener el TTL (Time To Live) corto, limitas el daño que un atacante puede hacer si logra robar el token.

El patrón de token de actualización (Refresh Token)

Para mantener una experiencia de usuario fluida sin aumentar los riesgos de seguridad, los desarrolladores a menudo usan una combinación de tokens de acceso de corta duración y tokens de actualización de mayor duración. El token de acceso otorga acceso inmediato a la API, mientras que el token de actualización se usa para obtener un nuevo token de acceso. Los tokens de actualización deben almacenarse de forma segura, a menudo en cookies HttpOnly, y tener un tiempo de expiración más largo.

5. Manejo de la revocación de tokens

Uno de los desafíos inherentes de los JWTs es que son sin estado. Una vez emitido, un token válido sigue siendo válido hasta que expira, incluso si el usuario cierra la sesión o cambia su contraseña. Esto hace que la revocación sea difícil.

Tiempos TTL cortos como estrategia de revocación

Como se mencionó, la forma más efectiva de manejar la revocación es mantener el tiempo de expiración muy corto. Si un token se compromete, el atacante solo tiene un tiempo limitado para usarlo.

Listas negras (Blacklists)

Para aplicaciones que requieren revocación inmediata, puedes implementar una lista negra con estado. Cuando un usuario cierra la sesión o su sesión se invalida, agregas el ID del token (JTI) a una base de datos o caché. Antes de validar un token, el servidor verifica si el JTI está en la lista negra. Sin embargo, este enfoque requiere mantener un estado, lo que contradice la naturaleza sin estado de los JWTs.

6. Valida las afirmaciones del token

Al recibir un JWT, tu servidor debe realizar una validación rigurosa. AWS detalla la importancia de validar las afirmaciones de Emisor (iss), Audiencia (aud) y Expiración (exp).

No validar estas afirmaciones puede llevar a omisiones de autorización, donde un usuario obtiene acceso a recursos a los que no debería tener acceso.

7. Considera alternativas como PASETO

Si bien los JWT son ampliamente utilizados, tienen una historia de fallos de seguridad. Por ejemplo, la vulnerabilidad del algoritmo "none" permitía a los atacantes falsificar tokens. HackerNoon discute el auge de PASETO (Platform for Authentication, Security, and Encryption of Tokens), un estándar de token más reciente diseñado para abordar las vulnerabilidades de JWT. PASETO admite tanto claves simétricas como asimétricas y está diseñado para ser más seguro por defecto. Aunque los JWT siguen siendo el estándar, entender alternativas como PASETO es crucial para arquitectos que toman decisiones a largo plazo.

Preguntas frecuentes

¿Cuál es la diferencia entre HS256 y RS256?

HS256 (HMAC con SHA-256) usa una clave secreta compartida entre el cliente y el servidor. RS256 (RSA con SHA-256) usa una clave privada para firmar el token y una clave pública para verificarlo. RS256 se considera generalmente más seguro para sistemas distribuidos porque la clave privada nunca sale del servidor.

¿Puedo revocar un JWT inmediatamente?

Debido a que los JWT son sin estado, técnicamente no puedes revocar un JWT inmediatamente sin mantener una lista negra o usar tiempos de expiración cortos. La solución más práctica es usar tokens de acceso de corta duración.

¿Es LocalStorage seguro para los JWTs?

No, LocalStorage es vulnerable a ataques XSS. Si tu aplicación se compromete, un atacante puede leer el LocalStorage y robar el JWT. Las cookies HttpOnly son el mecanismo de almacenamiento recomendado para la seguridad.

Conclusión

Proteger los JWTs requiere un enfoque multicapa. Al usar HTTPS, gestionando los secretos de forma segura, almacenando los tokens en cookies HttpOnly, implementando tiempos de expiración cortos y validando las afirmaciones rigurosamente, puedes reducir significativamente el riesgo de robo y mal uso de tokens. A medida que el panorama de la seguridad web evoluciona, mantenerse informado sobre las mejores prácticas y alternativas como PASETO es esencial para mantener una postura de seguridad robusta.

Referencias

  1. JWT vs PASETO: What's the Best Tool for Generating Secure Tokens?
  2. How to Build a Secure Nest.js REST API Using JWT and MongoDB
  3. How to secure API Gateway HTTP endpoints with JWT authorizer
  4. Best of 2021 – How to Revoke JSON Web Tokens (JWTs)
  5. JSON Web tokens (JWT): how to use them safely
  6. How to Create a JWT Secret in Node.js (Best Practices + Examples)
  7. How to monitor, optimize, and secure Amazon Cognito machine-to-machine authorization