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
- ¿Viene del issuer esperado?
- ¿Es el token type correcto?
- ¿Una herramienta/test generó un token unsigned?
- ¿El servidor solo está decodificando en vez de verificar?
- ¿La allowlist está configurada?
- ¿CI tiene test negativo para
none? - ¿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ón | Significado | Acción |
|---|---|---|
alg=none, API espera RS256 | policy violation | reject |
alg=none, perfil unsecured explícito | depende del perfil | seguir especificación |
| HS256 recibido, RS256 esperado | algoritmo no permitido | reject |
| signature segment vacío | sin autenticidad | reject |
alg desconocido | modo no permitido | fail closed |
Mi workflow seguro
- Defino algoritmos permitidos desde configuración confiable.
- Decodifico el header solo para diagnóstico.
- Comparo
algcon la allowlist. - Compruebo compatibilidad algoritmo/key type.
- Rechazo
nonecuando el perfil requiere firma. - Verifico con la key confiable esperada.
- Después valido
iss,aud, tiempo y token type. - Mantengo tests negativos para
none, cambio de algoritmo y payload manipulado. - Comparo configuración entre local, stage, gateway y producción si el comportamiento cambia por entorno.
- Monitorizo
algorithm_not_allowedsin almacenar tokens completos.
Regla final: el header describe lo que el token afirma; la configuración del servidor decide lo que puede aceptarse.