Si je vois :
{ "alg": "none" }
je le traite d’abord comme unsecured JWT : aucune signature cryptographique ne prouve l’intégrité ou l’origine. L’existence du format dans JOSE ne signifie pas qu’une API classique doit l’accepter.
JWT Security Analyzer signale alg: none lorsqu’un profil signé est attendu.
À quoi ressemble un JWT non signé
eyJhbGciOiJub25lIn0.eyJzdWIiOiI0MiJ9.
Il y a toujours trois segments, mais la signature est vide. Cela ne constitue aucune preuve d’authenticité.
Je distingue none d’un token malformed
alg: none+ segment signature vide ;- JWT avec mauvais nombre de segments ;
- JWS signé avec signature corrompue ;
- JWE à cinq segments.
JWT Decoder montre la structure, mais décoder ne vérifie pas.
Le header ne choisit jamais la policy
Mauvais modèle :
const algorithm = decodedHeader.alg;
verify(token, key, { algorithms: [algorithm] });
Modèle attendu :
verify(token, trustedKey, {
algorithms: ["RS256"]
});
La allowlist est une configuration serveur de confiance.
Exemple Node avec une allowlist et des claims fixes
Pour une API qui attend des access tokens RS256, la politique est explicite :
import jwt from "jsonwebtoken";
const payload = jwt.verify(token, trustedPublicKey, {
algorithms: ["RS256"],
issuer: "https://login.example.com/",
audience: "api://orders",
});
Je n’ajoute pas none pour faire passer un token inattendu. Si l’application accepte plusieurs profils JWT, je préfère des validateurs séparés par profil plutôt qu’une policy globale trop large.
Algorithm confusion est le problème général
alg none n’est qu’un cas. Le danger vient du fait de laisser un input non fiable choisir le mode cryptographique. RFC 8725 recommande une liste explicite d’algorithmes et une compatibilité stricte avec le type de clé.
Je traite aussi comme signal d’alerte :
API attend RS256 + public key
JWT annonce HS256
Je ne transforme pas une clé RSA en secret HMAC simplement parce que le header le demande.
Quand none peut exister légitimement
Certains profils spécialisés peuvent autoriser un unsecured JWT parce que l’intégrité est assurée ailleurs ou n’est pas requise. C’est une décision explicite de protocole.
Pour une API qui attend RS256 :
expected = RS256
received = none
=> reject
Je n’active jamais none simplement pour accepter un token externe.
Ce que j’enquête si none apparaît
- Le token vient-il de l’issuer attendu ?
- Est-ce le bon token type ?
- Un outil de test a-t-il généré un token unsigned ?
- Le serveur ne fait-il que decode au lieu de verify ?
- L’algorithm allowlist est-elle configurée ?
- CI contient-elle un test négatif pour
none? - Existe-t-il une route legacy ou un gateway avec une policy différente ?
Tests négatifs nécessaires
RS256 + signature valide -> accept si claims valides
RS256 + mauvaise signature -> reject
HS256 reçu pour API RS256 -> reject
alg none + signature vide -> reject
alg inconnu -> reject
payload modifié -> reject
Je teste la frontière de sécurité, pas seulement le happy path.
Un test d’intégration minimal peut rendre cette intention explicite :
expect(() => verifyAccessToken(unsignedToken)).toThrow();
expect(() => verifyAccessToken(hs256Token)).toThrow();
expect(() => verifyAccessToken(tamperedRs256Token)).toThrow();
Ces tests doivent échouer si quelqu’un élargit accidentellement la policy.
Je cherche aussi les regressions de configuration
Un alg:none accepté peut venir d’une différence entre environnements :
local: algorithms = [RS256]
stage: algorithms = [RS256]
prod: algorithms = undefined <- à examiner
Je vérifie les flags legacy, les middlewares dupliquées et les routes qui font uniquement decode() avant de faire confiance à sub, scope ou role.
Observabilité sans exposer les tokens
Pour enquêter, je journalise seulement :
- algorithme reçu ;
- algorithme attendu ;
- issuer attendu ;
- service/route ;
- request ID ;
- catégorie
algorithm_not_allowed.
Je n’ai pas besoin du JWT complet ni du matériel de clé pour savoir qu’une policy a refusé none.
Token type et claims restent nécessaires
Même avec le bon algorithme, je contrôle iss, aud, temps et profil. RFC 8725 recommande des règles séparées lorsque plusieurs types de JWT pourraient être confondus.
JWT Validator combine signature et claims.
Matrice de décision
| Observation | Sens | Action |
|---|---|---|
alg=none, API attend RS256 | violation de policy | reject |
alg=none, profil unsecured explicite | dépend du profil | suivre spécification |
| HS256 reçu, RS256 attendu | algorithme non autorisé | reject |
| signature vide dans profil signé | pas d’authenticité | reject |
| alg inconnu | mode non autorisé | fail closed |
Mon workflow sûr
- Je fixe les algorithmes autorisés dans la configuration de confiance.
- Je décode le header uniquement pour diagnostic.
- Je compare
algà l’allowlist. - Je contrôle compatibilité algorithm/key type.
- Je rejette
nonesi le profil exige une signature. - Je vérifie avec la clé de confiance prévue.
- Ensuite je valide
iss,aud, temps et token type. - Je garde des tests négatifs pour
none, changement d’algorithme et payload modifié. - Je compare la configuration entre local, stage, gateway et production si le comportement change.
- Je surveille
algorithm_not_allowedsans stocker les JWT complets.
Règle finale : le header décrit ce que le token prétend ; la configuration serveur décide ce qui est acceptable.