Contournement critique de l’authentification JWT dans pac4j avec une clé publique RSA (CVE-2026-29000)
- Loïc Castel
- il y a 16 heures
- 4 min de lecture
Une vulnérabilité d’authentification critique dans le module pac4j-jwt permet à un attaquant distant de s’authentifier comme n’importe quel utilisateur, y compris administrateur, en forgeant un JWT avec la seule clé publique RSA du serveur. JSON Web Token (JWT) est une norme ouverte (RFC 7519) qui définit une méthode compacte et autonome pour les transmissions sécurisées entre tiers d'informations encodées sous forme d'objet JSON. La signature numérique de ces informations vérifiables doit garantir leur fiabilité.
Le bug réside dans JwtAuthenticator lorsqu’il traite des JWT chiffrés (JWE), un flux particulier avec un PlainJWT non signé encapsulé dans un JWE fait sauter silencieusement toute vérification de signature, tout en continuant à construire un profil utilisateur à partir de revendications non vérifiées.

Sévérité : Critique (Score CVSS 3.1 : 10.0)
CVEs associées : CVE-2026-29000
Cibles version :
pac4j-jwt en version < 4.5.9 (4.x)
pac4j-jwt en version < 5.7.9 (5.x)
pac4j-jwt en version < 6.3.3 (6.x)
Action requise : Toute installation de pac4j-jwt utilisant JWE+JWS avec chiffrement RSA doit être mise à jour en urgence.
Des codes d'exploitation circulent déjà sur Internet au moment de la publication de ce blog post.
Contexte
pac4j est une bibliothèque Java de sécurité et de fédération d’identités très utilisée, dont le module pac4j-jwt gère l’authentification basée sur JSON Web Tokens (JWT). Dans de nombreux déploiements, pac4j-jwt est configuré pour combiner :
JWE (JSON Web Encryption) pour chiffrer le token avec la clé publique du serveur, assurant la confidentialité des revendications en transit.
JWS (JSON Web Signature) pour signer le JWT interne, garantissant l’intégrité et l’authenticité du token.
Le client envoie un JWE, le serveur le déchiffre avec la clé privée, extrait un JWT signé (JWS), vérifie la signature avec une configuration de signature, puis crée un profil utilisateur à partir des revendications si toutes les validations passent.
Conditions d’exploitation
Un déploiement est vulnérable lorsque les conditions suivantes sont réunies :
Version de pac4j-jwt inférieure à 4.5.9, 5.7.9 ou 6.3.3.
Utilisation de JwtAuthenticator pour authentifier des tokens JWT.
Tokens entrants chiffrés en JWE, et non uniquement signés.
Chiffrement JWE basé sur RSA.
Au moins une SignatureConfiguration configurée côté JWS.
L’attaquant peut récupérer la clé publique RSA (via JWKS, certificats TLS, documentation, dépôts publics, etc.).
Analyse du code vulnérable
Le problème se situe dans la façon dont JwtAuthenticator traite un JWE pour en extraire un JWT signé à l’aide de la bibliothèque Nimbus JOSE+JWT.
Le flux simplifié ressemble à ceci :
Le JWE est déchiffré via config.decrypt(encryptedJWT).
Le payload est converti en signedJWT via encryptedJWT.getPayload().toSignedJWT().
Si signedJWT n’est pas nul, la vérification de signature JWS est exécutée.
Quelle que soit la valeur de signedJWT, createJwtProfile(ctx, credentials, jwt) est appelé ensuite.

Le point critique est le comportement de toSignedJWT() qui renvoie null lorsqu’il s’agit d’un PlainJWT encapsulé dans le JWE, ce qui désactive toute vérification de signature. Le code continue néanmoins son exécution et construit un profil à partir d’un JWT jamais validé cryptographiquement.
PlainJWT dans un JWE : la primitive d’attaque
La RFC 7519 prévoit un troisième type de JWT, le PlainJWT, c’est-à-dire un token sans signature. De nombreuses bibliothèques continuent de le supporter pour des raisons de compatibilité, notamment en combinaison avec un JWE qui assure déjà la confidentialité.
JwtAuthenticator suppose implicitement que tout JWE valide contient un JWS à vérifier, hypothèse qui n’est jamais imposée par une vérification explicite du type de JWT. Un attaquant capable de choisir le type de JWT interne peut donc insérer un PlainJWT, provoquer signedJWT == null, et désactiver ainsi entièrement la vérification JWS tout en contrôlant les revendications passées à createJwtProfile.
Surface d’attaque et obtention de la clé publique
L’attaque repose uniquement sur la possession de la clé publique RSA utilisée pour chiffrer les JWE, cette clé est souvent exposée de manière légitime dans l’écosystème JWT :
Endpoint JWKS publiés sous /.well-known/jwks.json
Extraction de la clé publique depuis un certificat TLS, si elle est réutilisée pour le JWE (mauvaise pratique, mais courante).
Fichiers de configuration, documentation, exemples de code ou dépôts Git publics qui contiennent la paire de clés de dev ou de test.
Une fois cette clé publique récupérée, l’attaquant peut construire des JWE que le serveur déchiffre correctement, sans jamais avoir eu accès à la clé privée.
Envoi du JWE forgé et contournement de l’authentification
L’attaquant construit un JWE contenant un PlainJWT avec les revendications de son choix, par exemple un sujet correspondant à un compte administrateur et des rôles élevés. Ce token est envoyé via le flux d’authentification habituel (en-tête Authorization: Bearer ou paramètre de requête selon l’intégration).
Dans la version vulnérable de JwtAuthenticator :
La couche JWE valide la décryption : le token passe la barrière de confidentialité.
La tentative de conversion en SignedJWT échoue et renvoie null .
La vérification de signature JWS est conditionnée par if (signedJWT != null) et est donc entièrement sautée.
createJwtProfile est appelé avec le JWT non signé, et les revendications malveillantes sont acceptées comme authentifiées.
L’attaquant obtient alors une session ou un contexte d’authentification associé à l’utilisateur et aux rôles choisis dans le PlainJWT, sans jamais avoir connu un mot de passe, un secret de signature ou la clé privée du serveur.
Recommandations
Les recommandations sont les suivantes :
Mettre à jour immédiatement vers une version corrigée.
Revoir la configuration pour s’assurer qu’elle correspond bien au modèle de sécurité voulu (JWS-only, JWE+JWS, etc.) et que les types de tokens non signés sont interdits dans les flux sensibles.
Conclusion
Cette vulnérabilité montre à quel point une hypothèse implicite sur la forme des JWT (présumés toujours signés) peut suffire à casser entièrement le modèle de sécurité d’une application utilisant pac4j-jwt. Elle rappelle la nécessité de traiter explicitement les types de tokens acceptés, de vérifier systématiquement les propriétés cryptographiques attendues, et de maintenir à jour les bibliothèques de sécurité.
En cas de doute ou suspicion de compromission, il est recommandé de faire appel à des équipes de réponse à incident ou threat hunters.
Si vous souhaitez scanner / vérifier la vulnérabilité de vos services, Safercy bénéficie d'une équipe de penetration testers reconnue et accréditée.
La mise à jour vers les versions corrigées, combinée à une revue de configuration et à une interdiction stricte des PlainJWT dans les flux sensibles, est indispensable pour rétablir un niveau de confiance acceptable dans l’authentification.



