Json Web Token (JWT): Qué es, cómo funciona y buenas prácticas de seguridad

¿Te has preguntado cómo las aplicaciones modernas gestionan la autenticación y autorización de usuarios de manera segura? En este blog, exploraremos en profundidad los JSON Web Tokens (JWT), una tecnología clave para la gestión de identidades en aplicaciones web y móviles. Aprenderás qué son los JWT, cómo se estructuran, los algoritmos de firma más comunes, y las mejores prácticas para asegurar su implementación.

Autor: José Alvarado
Publicado: hace alrededor de 1 mes
Json Web Token (JWT): Qué es, cómo funciona y buenas prácticas de seguridad

Introducción

¿Qué es un JSON Web Token (JWT)?

Un JSON Web Token (JWT) es un estándar abierto (RFC 7519) que define un método compacto y seguro para transmitir información entre dos partes en formato JSON.

Este token se usa principalmente en autenticación y autorización, permitiendo que un servidor valide la identidad de un usuario sin necesidad de almacenar sesiones en el backend.

¿Para qué se usa JWT?

Algunos casos de uso comunes incluyen:

  • Autenticación: permite verificar la identidad de un usuario. Un usuario inicia sesión, recibe un token y lo usa para acceder a recursos protegidos
  • Autorización: se incluyen roles y permisos dentro del token para determinar qué acciones puede realizar un usuario
  • Intercambio de información: se pueden incluir datos adicionales en el token, como el nombre del usuario, su correo electrónico, etc.

Diferencia entre JWT y otros métodos de autenticación

Existen otros métodos de autenticación, como las cookies de sesiones y OAuth, pero cada una tiene sus propias características y limitaciones. A continuación te presento algunas diferencias:

  • Cookies y sesiones: se almacena la sesión en el servidor y la cookie en el cliente. Es seguro pero no escalable en sistemas distribuidos
  • JWT: La información viaja en el token, sin necesidad de almacenar sesiones. Es escalable y sin estado, pero requiere cuidado en la implementación por el riesgo en que se exponga el token
  • OAuth: es un protocolo de autorización basado en tokens. Es seguro y estandar para aplicaciones de terceros, pero más complejo de implementar. Un ejemplo de uso es cuando podemos acceder a una aplicación con nuestra cuenta de Google o Facebook

Estructura de un JWT

Un JWT consta de tres partes separadas por puntos (.): el encabezado, la carga útil y la firma. A continuación, se muestra un ejemplo de un JWT:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

El token se divide de la siguiente manera:

1. Encabezado (Header): contiene el tipo de token y el algoritmo de firma utilizado. Por ejemplo:

{
  "alg": "HS256",
  "typ": "JWT"
}

2. Carga útil (Payload): contiene la información que se desea transmitir. Por ejemplo:

{
  "userId": "123456",
  "role": "admin",
  "iat": 1674636000,
  "exp": 1674640000
}

3. Firma (Signature): asegura la autenticidad del token mediante un algoritmo de firma. Por ejemplo:

HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  secret
)

Algoritmos de firma en JWT

Los JWT pueden firmarse con algoritmos simétricos (HMAC - Hash-based Message Authentication Code) o asimétricos (RSA - Rivest-Shamir-Adleman y ECDSA - Elliptic Curve Digital Signature Algorithm).

HMAC (Hash-based Message Authentication Code)

El algoritmo HMAC utiliza una clave secreta compartida entre el emisor y el receptor para firmar y verificar el token. Los tres tipos más comunes de HMAC son:

  • HS256: utiliza el algoritmo HMAC con SHA-256, donde el número indica el tamaño del hash (256 bits)
  • HS384: utiliza el algoritmo HMAC con SHA-384, donde el número indica el tamaño del hash (384 bits)
  • HS512: utiliza el algoritmo HMAC con SHA-512, donde el número indica el tamaño del hash (512 bits)

RSA (Rivest-Shamir-Adleman)

El algoritmo RSA utiliza un par de claves (pública y privada) para firmar y verificar el token. Los tres tipos más comunes de RSA son:

  • RS256: utiliza el algoritmo RSA con SHA-256, donde el número indica el tamaño del hash (256 bits)
  • RS384: utiliza el algoritmo RSA con SHA-384, donde el número indica el tamaño del hash (384 bits)
  • RS512: utiliza el algoritmo RSA con SHA-512, donde el número indica el tamaño del hash (512 bits)

ECDSA (Elliptic Curve Digital Signature Algorithm)

El algoritmo ECDSA es similar al RSA, pero utiliza curvas elípticas en lugar de números enteros para firmar y verificar el token. Los tres tipos más comunes de ECDSA son:

  • ES256: utiliza el algoritmo ECDSA con SHA-256, donde el número indica el tamaño del hash (256 bits)
  • ES384: utiliza el algoritmo ECDSA con SHA-384, donde el número indica el tamaño del hash (384 bits)
  • ES512: utiliza el algoritmo ECDSA con SHA-512, donde el número indica el tamaño del hash (512 bits)

Consideraciones de seguridad

  • HS256: se considera el algoritmo más seguro y se recomienda su uso siempre que sea posible
  • RS256: se utiliza en aplicaciones que requieren firmas digitales y verificación de identidad
  • ES256: se utiliza en aplicaciones que requieren alta seguridad y rendimiento

Se recomienda evitar el uso de algoritmos más débiles como HS512 y HS384 a menos que sea absolutamente necesario.

Seguridad en JWT

Riesgos y vulnerabilidades comunes

  • JWT sin firma o con firmas débiles: un token sin firma o con una firma débil es vulnerable a ataques de falsificación y manipulación
  • Exposición del payload: los datos en el payload no están cifrados, solo codificados en Base64. Si el payload contiene información sensible, como contraseñas o tokens de acceso, es importante cifrarlo o evitar su exposición
  • Robo de tokens y ataques de replay: los tokens robados pueden ser utilizados por un atacante para acceder a recursos protegidos. Se recomienda utilizar tokens de corta duración y renovarlos periódicamente

Buenas prácticas de seguridad

Usar expiración (exp) y otros claims

Se recomienda incluir una fecha de expiración (exp) y otros claims como ìat (fecha de emisión) y nbf (fecha de inicio de validez) para limitar la vida útil del token y prevenir ataques de replay.

{
  "exp": 1674640000, // Fecha de expiración
  "iat": 1674636000, // Fecha de emisión
  "nbf": 1674636000 // Fecha de inicio de validez
}

Almacenamiento seguro del token

Existen varias formas de almacenar un JSON Web Token (JWT), cada una con ventajas y desventajas. La elección depende del nivel de seguridad requerido en la aplicación. Algunas opciones comunes son:}

LocalStorage

  • Es accesible desde JavaScript y persiste incluso después de cerrar el navegador
  • Riesgo: es vulnerable a ataques Cross-Site Scripting (XSS), lo que significa que un atacante podría robar el token si logra inyectar código malicioso en la página.
  • Recomendación: No almacenar tokens en LocalStorage en aplicaciones sensibles, ya que el riesgo de robo es alto. Si se usa, considera técnicas de mitigación como Content Security Policy (CSP) o cifrado del token, aunque esto no elimina completamente el riesgo.

SessionStorage

  • Es similar a LocalStorage, pero los datos se eliminan al cerrar la pestaña o el navegador
  • Riesgo: también es vulnerable a ataques XSS, aunque la exposición es menor debido a su naturaleza temporal.
  • Recomendación: se puede usar en escenarios donde la sesión no debe persistir y el impacto de un ataque XSS sea bajo. Sin embargo, sigue sin ser la opción más segura.

HTTPOnly Cookies (opción recomendada)

  • Son cookies que el navegador no permite acceder desde JavaScript, lo que las hace seguras contra XSS. Solo se envían al servidor en cada solicitud HTTP
  • Riesgo: son vulnerables a ataques Cross-Site Request Forgery (CSRF) si no se configuran adecuadamente
  • Recomendación: para mitigar riesgos de XSS y CSRF, configura las cookies con las siguientes opciones:
    • HttpOnly: Impide el acceso desde JavaScript, protegiéndolas contra XSS
    • Secure: Garantiza que solo se envíen a través de conexiones HTTPS
    • SameSite: Limita el envío de cookies a solicitudes del mismo sitio para prevenir ataques CSRF. Puedes configurarlo según tus necesidades:
      • SameSite=Strict: si requieres máxima seguridad pero puede afectar la usabilidad
      • SameSite=Lax: si necesitas un equilibrio entre seguridad y usabilidad
      • Evita SameSite=None a menos que sea estrictamente necesario, ya que puede exponer las cookies a ataques CSRF

Rotación de tokens y uso de Refresh Tokens

  • Rotación de tokens: consiste en invalidar el refresh token anterior y emitir uno nuevo cada vez que se usa para obtener un nuevo access token. Esto reduce el riesgo de reutilización de tokens comprometidos y mejora la seguridad.

  • Refresh Tokens: son credenciales de largo plazo utilizadas para obtener nuevos access tokens sin que el usuario deba ingresar credenciales nuevamente. Se recomienda usarlos en combinación con tokens de acceso de corta duración para minimizar el impacto de un robo. Para mejorar la seguridad, deben almacenarse de manera segura y rotarse periódicamente para evitar ataques de reutilización.

{
  "accessToken": "JWT_de_corto_plazo",
  "refreshToken": "JWT_de_largo_plazo"
}
Consideraciones adicionales
  • El refresh token debe ser transmitido solo en solicitudes seguras (HTTPS)
  • En aplicaciones web, es mejor enviarlo en una httpOnly cookie para evitar ataques XSS
  • Los access tokens no deben almacenarse en localStorage por razones de seguridad

Conclusión

JSON Web Tokens (JWT) son una solución eficiente para autenticación y autorización, pero deben usarse con precaución. Implementar buenas prácticas de seguridad como expiración, almacenamiento seguro y algoritmos de firma adecuados puede proteger tu aplicación contra ataques.

¡No te pierdas mi próximo blog, haremos una práctica de implementación de JWT en una aplicación web! 🚀