Regex JWT (JSON Web Token)
L'auteur d'une question Stack Overflow voulait masquer les JWT dans ses logs, mais sa regex capturait « toute chaîne à deux points » — y compris des paramètres d'URL n'ayant rien à voir avec un token. Pire : notre analyse ReDoS la classe vulnérable (complexité polynomiale), un défaut sérieux pour une regex censée tourner sur des lignes de log potentiellement influencées par un attaquant. Une réponse corrige le problème de portée en exigeant que les deux premiers segments commencent par « eyJ » (le début Base64URL de « {" »)... mais un commentaire signale, à raison, que tous les JWT ne commencent pas forcément par « eyJ ». Voici un motif sûr qui vérifie la vraie structure (3 segments Base64URL sans le remplissage « = »), comparé à ces deux tentatives et à une troisième, plus permissive, qui accepte aussi la forme à 5 segments d'un JWE.
/^[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]*$/Comparatif des patterns existants
| Source | pattern | Score sur le corpus | ReDoS |
|---|---|---|---|
| Stack Overflow — regex de l'auteur de la question, démontrée dans la question elle-même comme capturant n'importe quelle chaîne à deux points | [A-Za-z0-9-_=]+\.[A-Za-z0-9-_=]+\.?[A-Za-z0-9-_.+/=]* | 12 / 20 | vulnérable (------------------------------------------------------------…) |
| Stack Overflow — réponse à 3 votes, exige le préfixe littéral « eyJ » (signalé en commentaire comme non garanti pour tout JWT), et hérite du point erroné de la question dans la classe du 3e segment | ^eyJ[A-Za-z0-9-_]+\.eyJ[A-Za-z0-9-_]+\.[A-Za-z0-9-_.+/]*$ | 15 / 20 | sûr |
| Stack Overflow — réponse étendue, accepte aussi la forme à 5 segments d'un JWE (trop permissif pour une validation JWS à 3 segments) | ^e[yw][A-Za-z0-9-_]+\.(?:e[yw][A-Za-z0-9-_]+)?\.[A-Za-z0-9-_]{2,}(?:(?:\.[A-Za-z0-9-_]{2,}){2})?$ | 15 / 20 | sûr |
Implémentation
const re = new RegExp("^[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]*$", "");
re.test(input);Limites
- Valide uniquement la FORME (3 segments Base64URL séparés par des points, sans remplissage « = ») : ne décode ni ne vérifie qu'un segment contient réellement du JSON valide, et encore moins que la signature correspond à la clé attendue.
- Scope limité à la sérialisation compacte JWS (3 segments) : le format JWE (chiffrement, 5 segments) suit une structure différente et n'est pas couvert par ce motif.
- Le troisième segment (signature) est autorisé à être vide pour couvrir le cas valide « alg: none » (JWT non signé, RFC 7515) : dans un contexte où seuls des tokens signés sont acceptables, ce cas devrait être rejeté séparément, en dehors de la regex elle-même.
- N'impose aucune contrainte sur la longueur des segments : un JWT syntaxiquement valide selon ce motif peut être arbitrairement court et ne représenter aucune donnée exploitable une fois décodé.