Saltar al contenido

Mejores Prácticas de Seguridad de JWT | Ataque alg none / Vencimiento / Verificación de Firma

Categoría: Autenticación / Seguridad
Este artículo está disponible actualmente solo en japonés. Estamos trabajando en las traducciones.

JWT (JSON Web Token) es el estándar de facto para la autenticación en API web modernas, pero <strong>implementarlo mientras se malinterpreta la especificación puede introducir fácilmente vulnerabilidades</strong>. Este artículo explica patrones de mal uso común de JWT y mejores prácticas para evitarlos.

Repaso de la Estructura de JWT

JWT es un formato que concatena tres cadenas codificadas en Base64URL con puntos.

eyJhbGci...    .   eyJzdWIi...     .    SflKxwRJSM...
   header              payload                signature
  • <strong>header</strong>: Algoritmo de firma (<code>alg</code>) y tipo de token (<code>typ</code>)
  • <strong>payload</strong>: Objeto JSON de reclamaciones (sub / iss / aud / exp / iat / ...)
  • <strong>signature</strong>: Firma HMAC / RSA / ECDSA sobre header y payload

Puede verificar el contenido de un JWT usando <a href="/ja/tools/jwt/">DevLab JWT Decoder</a>. Recuerde que Base64URL es codificación, no <strong>encriptación</strong>, por lo que el contenido del payload puede ser leído por cualquiera sin verificación de firma del servidor.

Amenaza 1: ataque alg=none

La especificación JWT incluye <code>"alg": "none"</code>, una designación de algoritmo "sin firma". Si un atacante explota esto para modificar el payload con un encabezado <code>{"alg":"none"}</code>, y la biblioteca lo acepta tal cual, se convierte en una <strong>vulnerabilidad crítica que permite la suplantación de cualquier usuario</strong>.

<strong>Contramedida:</strong>

  • <strong>Enumerar explícitamente en lista blanca</strong> los algoritmos permitidos durante la verificación
  • Pasar como una matriz como <code>jwt.verify(token, secret, { algorithms: ['HS256'] })</code>
  • Las bibliotecas alrededor de 2015 eran vulnerables, pero las bibliotecas principales actuales ya han implementado soluciones. Sin embargo, siempre use la versión más reciente a menos que esté creando desde cero.
// ✗ 悪い例 (アルゴリズム未指定 = ライブラリが alg ヘッダを信頼)
jwt.verify(token, secret);

// ✓ 良い例 (アルゴリズムを固定)
jwt.verify(token, secret, { algorithms: ['HS256'] });

Amenaza 2: ataque de confusión de claves (key confusion)

Un atacante que posee una clave pública RSA puede <strong>cambiar el algoritmo de RS256 a HS256</strong> para forzar que la clave pública sea tratada como una "clave secreta". Si la biblioteca JWT confía en el campo <code>alg</code> al seleccionar la función de verificación, los tokens falsificados firmados con HMAC usando la clave pública serán aceptados.

<strong>Contramedida:</strong> Siempre fija el algoritmo en el lado de la verificación (misma contramedida que la amenaza 1). Además, distingue explícitamente el tipo de clave:

  • Para HS256, pasa como <code>Buffer</code>
  • Para RS256, pase la clave pública en formato PEM

Amenaza 3: Expiración no verificada / tokens indefinidos

El claim <code>exp</code> en JWT indica el tiempo de vencimiento en segundos UNIX, pero <strong>a menudo se ignora durante la validación</strong>. Si un token emitido una vez se puede usar indefinidamente, el daño de una violación no se puede contener.

<strong>Contramedida:</strong>

  • Establezca <code>exp</code> en una duración corta al emitir (15 minutos a 1 hora es lo recomendado para tokens de acceso)
  • Siempre verifique <code>exp</code> durante la verificación (las bibliotecas principales lo hacen automáticamente)
  • Para sesiones de larga duración, use el patrón de token de actualización (token de acceso de corta vida + token de actualización de larga vida + lista de revocación del lado del servidor)

Amenaza 4: Almacenar información sensible en JWT

El payload de JWT es simplemente codificación Base64, y cualquiera puede decodificarlo. A pesar de esto, las implementaciones que <strong>ponen contraseñas o números de tarjeta de crédito en el payload</strong> continúan apareciendo.

<strong>Contramedida:</strong>

  • Incluir solo información de identificación mínima en el payload indicando 「este usuario es…」 (sub / user_id / role)
  • Almacenar información confidencial en una base de datos del lado del servidor y recuperarla de JWT usando user_id
  • Si debe enviar información sensible en un JWT, use <strong>JWE</strong> (JWT cifrado)

Amenaza 5: Incapaz de revocar (logout)

Cuando JWT se emite sin estado, no se puede revocar. Incluso si un usuario cierra sesión, como el servidor no tiene información del token, el token "se puede usar hasta el vencimiento". Incluso después de un cambio de contraseña, los JWT emitidos siguen siendo válidos.

<strong>Contramedida:</strong>

  • Minimizar la ventana de daño con <code>exp</code> corto (15 minutos o menos)
  • Al cerrar sesión, registre el <code>jti</code> (JWT ID) en la <strong>lista de denegación (denylist)</strong> del lado del servidor y compárelo durante la validación
  • Para eventos críticos (cambio de contraseña, cambio de permiso), almacene <code>token_version</code> en el registro del usuario y verifique una coincidencia durante la validación

Amenaza 6: Secretos débiles

Si el secreto HS256 es demasiado corto, puede ser descifrado por fuerza bruta. Cadenas como <code>"secret"</code> y <code>"password123"</code> son particularmente vulnerables.

<strong>Contramedida:</strong>

  • HS256 debe usar al menos <strong>256 bits (32 bytes)</strong> de valores aleatorios
  • Generar con <code>openssl rand -base64 32</code> o la <a href="/ja/tools/password/">herramienta de generación de contraseñas</a>
  • Nunca confirme secretos en Git. Adminístrelos con variables de entorno o Secrets Manager

Resumen de mejores prácticas

  1. Fijar el algoritmo en el lado de verificación (<code>algorithms: ['HS256']</code>)
  2. El secreto HS256 debe tener 256 bits o más, RS256 debe usar RSA con 2048 bits o más
  3. El <code>exp</code> del token de acceso es de 15 minutos a 1 hora
  4. Implementar sesiones de larga duración con patrón de token de actualización
  5. No incluir información sensible en el payload (Base64 no es cifrado)
  6. Al cerrar sesión, registre <code>jti</code> en la lista de denegación
  7. Gestione secretos con variables de entorno o Secrets Manager; nunca los confirme
  8. Almacene JWT en una cookie HttpOnly, no en localStorage, en el lado del cliente (contramedida XSS)

Herramientas útiles para depuración

  • <a href="/ja/tools/jwt/">Decodificador JWT</a>: visualiza header / payload / signature con explicaciones del significado de cada claim
  • <a href="/ja/tools/jwt-sign/">Herramienta de Generación y Firma de JWT</a>: Firma usando Web Crypto API en tu navegador con HS256 / HS384 / HS512. Conveniente para generar tokens de prueba
  • <a href="/ja/tools/password/">Generador de Contraseñas</a>: se puede usar para generar secretos

Resumen

JWT es un token de autenticación conveniente si se usa correctamente, pero sin conocer los peligros de especificación y los patrones de ataque, corre el riesgo de introducir vulnerabilidades en sistemas de producción. Comprenda las 6 amenazas y contramedidas presentadas en este artículo y use la última versión de las bibliotecas JWT como base. Le recomendamos que revise regularmente su implementación y monitoree los avisos de seguridad.