Guías JWT

Cómo trato JWT alg none sin confiar en él por accidente

Qué significa alg none, por qué un JWT sin firma no demuestra integridad y cómo fijo algoritmos permitidos desde configuración confiable.

Por JWTDecoder.toolsÚltima verificación 2026-09-16

Voz editorial: la primera persona describe el flujo de trabajo documentado de JWTDecoder.tools; JWTDecoder.tools es el editor y autor organizacional.

alg=none ∉ application allowlist
token alg → application allowlist → accept / reject

Si veo:

{ "alg": "none" }

lo trato primero como unsecured JWT: no hay firma criptográfica que pruebe integridad ni origen. Que el formato exista en JOSE no significa que una API normal deba aceptarlo.

JWT Security Analyzer marca alg: none cuando el contexto esperado es un token firmado.

Cómo se representa un JWT sin firma

eyJhbGciOiJub25lIn0.eyJzdWIiOiI0MiJ9.

Sigue habiendo tres segmentos, pero la firma está vacía. Eso no es evidencia de autenticidad.

Distingo none de un token malformed

No son lo mismo:

  • alg: none + signature segment vacío;
  • JWT con dos/cuatro segmentos;
  • JWS firmado con firma corrupta;
  • JWE de cinco segmentos.

JWT Decoder ayuda a ver la forma, pero decode no verifica confianza.

No dejo que el header elija la política

Código peligroso:

const algorithm = decodedHeader.alg;
verify(token, key, { algorithms: [algorithm] });

La configuración segura define la allowlist:

verify(token, trustedKey, {
  algorithms: ["RS256"]
});

Si llega none, HS256 u otro algoritmo fuera de policy, rechazo.

Ejemplo con jsonwebtoken: allowlist fija y claims esperados

Cuando una API Node espera access tokens RS256, la decisión vive en configuración de servidor, no en el JWT:

import jwt from "jsonwebtoken";

const payload = jwt.verify(token, trustedPublicKey, {
  algorithms: ["RS256"],
  issuer: "https://login.example.com/",
  audience: "api://orders",
});

No añado none a algorithms para “hacer funcionar” un token de origen dudoso. Si el sistema usa varias familias de tokens, prefiero validators separados por perfil antes que una lista amplia que acepte combinaciones no previstas.

Algorithm confusion es el problema más general

alg none es solo un caso. El riesgo aparece cuando input no confiable selecciona el modo criptográfico. RFC 8725 recomienda fijar explícitamente algoritmos permitidos y asegurar compatibilidad con el tipo de clave.

Por eso también considero sospechoso:

API espera RS256 + public key
JWT declara HS256

No reutilizo material RSA como secret HMAC ni cambio de verifier solo porque el header lo pida.

Cuándo puede existir none de forma legítima

Hay perfiles especiales donde un unsecured JWT puede tener sentido porque autenticidad/integridad se resuelven fuera del JWT o no se requieren. Esa es una decisión explícita del protocolo.

En una API que espera access tokens RS256:

expected = RS256
received = none
=> reject

No habilito none solo para hacer pasar un token externo.

Qué investigo cuando aparece inesperadamente

  1. ¿Viene del issuer esperado?
  2. ¿Es el token type correcto?
  3. ¿Una herramienta/test generó un token unsigned?
  4. ¿El servidor solo está decodificando en vez de verificar?
  5. ¿La allowlist está configurada?
  6. ¿CI tiene test negativo para none?
  7. ¿Existe otro verifier con una policy más permisiva en gateway o servicio legacy?

Tests negativos que quiero ver

RS256 firma válida        -> accept si claims pasan
RS256 firma incorrecta    -> reject
HS256 en API RS256        -> reject
alg none + firma vacía    -> reject
alg desconocido           -> reject
payload manipulado        -> reject

No pruebo únicamente el happy path.

Un test de integración mínimo puede expresarlo así:

expect(() => verifyAccessToken(unsignedToken)).toThrow();
expect(() => verifyAccessToken(hs256Token)).toThrow();
expect(() => verifyAccessToken(tamperedRs256Token)).toThrow();

La intención es importante: los tests deben fallar si alguien ensancha la policy por accidente.

Detecto regressions de configuración, no solo tokens malos

Un incidente con alg:none puede ser un problema de despliegue más que de emisor. Reviso diferencias entre entornos:

local: algorithms = [RS256]
stage: algorithms = [RS256]
prod:  algorithms = undefined   <- revisar

También busco flags de compatibilidad legacy, middleware duplicada o una ruta que haga decode() y confíe directamente en sub, role o scope.

Qué registro sin filtrar secretos

Para investigar una violación de algorithm policy, basta con metadata no sensible:

  • algoritmo recibido;
  • algoritmo esperado;
  • issuer esperado;
  • ruta/servicio;
  • request ID;
  • categoría algorithm_not_allowed.

No necesito registrar el JWT completo ni material de clave.

También valido token type y claims

Incluso con algoritmo permitido todavía debo validar iss, aud, tiempo y el perfil del token. RFC 8725 recomienda reglas de validación mutuamente exclusivas cuando distintos tipos de JWT pueden confundirse.

JWT Validator combina firma y claims.

Matriz de decisión

ObservaciónSignificadoAcción
alg=none, API espera RS256policy violationreject
alg=none, perfil unsecured explícitodepende del perfilseguir especificación
HS256 recibido, RS256 esperadoalgoritmo no permitidoreject
signature segment vacíosin autenticidadreject
alg desconocidomodo no permitidofail closed

Mi workflow seguro

  1. Defino algoritmos permitidos desde configuración confiable.
  2. Decodifico el header solo para diagnóstico.
  3. Comparo alg con la allowlist.
  4. Compruebo compatibilidad algoritmo/key type.
  5. Rechazo none cuando el perfil requiere firma.
  6. Verifico con la key confiable esperada.
  7. Después valido iss, aud, tiempo y token type.
  8. Mantengo tests negativos para none, cambio de algoritmo y payload manipulado.
  9. Comparo configuración entre local, stage, gateway y producción si el comportamiento cambia por entorno.
  10. Monitorizo algorithm_not_allowed sin almacenar tokens completos.

Regla final: el header describe lo que el token afirma; la configuración del servidor decide lo que puede aceptarse.

Fuentes y referencias

Guías relacionadas