Guides JWT

Comment je traite JWT alg none sans lui faire confiance

Ce que signifie alg none, pourquoi un JWT non signé ne prouve aucune intégrité et comment fixer les algorithmes autorisés dans la configuration de confiance.

Par JWTDecoder.toolsDernière vérification le 2026-09-16

Voix éditoriale : la première personne décrit le workflow documenté de JWTDecoder.tools ; JWTDecoder.tools est l’éditeur et l’auteur organisationnel.

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

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

  1. Le token vient-il de l’issuer attendu ?
  2. Est-ce le bon token type ?
  3. Un outil de test a-t-il généré un token unsigned ?
  4. Le serveur ne fait-il que decode au lieu de verify ?
  5. L’algorithm allowlist est-elle configurée ?
  6. CI contient-elle un test négatif pour none ?
  7. 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

ObservationSensAction
alg=none, API attend RS256violation de policyreject
alg=none, profil unsecured explicitedépend du profilsuivre spécification
HS256 reçu, RS256 attendualgorithme non autoriséreject
signature vide dans profil signépas d’authenticitéreject
alg inconnumode non autoriséfail closed

Mon workflow sûr

  1. Je fixe les algorithmes autorisés dans la configuration de confiance.
  2. Je décode le header uniquement pour diagnostic.
  3. Je compare alg à l’allowlist.
  4. Je contrôle compatibilité algorithm/key type.
  5. Je rejette none si le profil exige une signature.
  6. Je vérifie avec la clé de confiance prévue.
  7. Ensuite je valide iss, aud, temps et token type.
  8. Je garde des tests négatifs pour none, changement d’algorithme et payload modifié.
  9. Je compare la configuration entre local, stage, gateway et production si le comportement change.
  10. Je surveille algorithm_not_allowed sans 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.

Sources et références

Guides associés