CVE-2026-114615: Authlib incomplete algorithm-confusion fix permits HMAC forgery with prefixed public keys
Affects authlibSource GCVE Database
Mentioned vulnerabilities
Not known to be exploited · CVSS 7.4 High : upgrade to 1.9.0 soon.
Data as of 27 Oct 2026, 16:27 UTC
- Patch
- Available – fixed in 1.9.0
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:NExploitation evidence
Disclosed: evidence found.
CVE published 22 Oct 2026
Detectable: no evidence on record.
No public nuclei template indexed
Weaponised: no evidence on record.
No public exploit code indexed in Metasploit or Exploit-DB
Exploited in the wildExploited: no evidence on record.
Not in CISA KEV and no source reports exploitation
Observed by OffSeq MirageSeen by Mirage: no evidence on record.
No attack traffic recorded by OffSeq Mirage sensors
A missing step is not evidence of safety. Sources: cve.org (opens in a new tab), the Metasploit, Exploit-DB and nuclei-templates index as of 26 Oct 2026.
What to do
Upgrade authlib to 1.9.0.
Affected software
| Product | Affected | Fixed in |
|---|---|---|
| PyPIauthlib | Affected
| Fixed in
|
Check your own hosts for these packages with threat-finder (opens in a new tab), our free open-source scanner.
Description
## Summary `OctKey.import_key()` carries the only guard that stops an asymmetric public key from being used as an HMAC secret in `authlib.jose`. That guard is `raw_key.startswith(POSSIBLE_UNSAFE_KEYS)` — anchored at byte 0 — while the loader that consumes the identical bytes on the asymmetric path (`cryptography.hazmat.primitives.serialization.load_pem_public_key` / `load_pem_x509_certificate`) skips anything before the `-----BEGIN` line. A PEM public key or X.509 certificate carrying **one leading byte** — a newline, a space, four spaces of indentation, a UTF-8 BOM, or a comment line — is therefore not recognised, falls through, and becomes the HMAC secret. An attacker holding only the issuer's public key then mints an `HS256` token with arbitrary claims that `jwt.decode()` accepts as authentic. This is an incomplete fix of **CVE-2024-37568 / GHSA-5357-c2jx-v7qh**. That advisory's fix is exactly this guard (`git log -S 'POSSIBLE_UNSAFE_KEYS' -- authlib/jose/rfc7518/oct_key.py` returns a single commit, `3bea812` "fix: prevent OctKey to import ssh/rsa/pem keys", referencing issue #654), and the original attack works again unchanged whenever the stored PEM does not start at offset 0. Reproduced end-to-end on **1.8.0**, the newest release (published 2026-08-30), and on `origin/main` (`c529a61`), with `cryptography` 50.0.2. ## Preconditions Both are deployment properties; neither is attacker-controlled at request time, and the attacker needs no secret and no user interaction. 1. **The relying party uses the documented module-level `jwt` object.** `from authlib.jose import jwt` is built at `authlib/jose/__init__.py:55-72` with the full list `["HS256","HS384","HS512","RS256",...,"EdDSA"]`, and `JsonWebToken.decode` (`authlib/jose/rfc7519/jwt.py:80`) exposes **no `algorithms` parameter**, so a caller of the documented object has no way to narrow it. This is the first code sample in `docs/jose/jwt.rst:21-31`. This hazard is already documented: `docs/jose/jwt.rst:73-79` says "This decoding method is insecure. By default `jwt.decode` parses the alg header. This allows symmetric macs and asymmetric signatures.", and `docs/jose/jwt.rst:94-98` says "You should never combine symmetric (HS) and asymmetric (RS, ES, PS) signature schemes." The point of this report is **not** that the mixed list exists — it is that the `oct_key.py:81` backstop shipped for precisely that documented hazard does not hold. 2. **The verification key is passed as PEM/certificate text whose bytes do not begin at offset 0 with `-----BEGIN `.** Each of the following forms was confirmed to forge, and each also still verifies a genuine RS256/ES256 token with the identical bytes, so nothing in the deployment looks broken: * one leading newline — what a triple-quoted Python literal produces * one leading space * four-space indentation — an indented YAML/JSON/TOML config block * a UTF-8 BOM — a Windows-editor artifact * a leading comment line * all of the above applied to an X.509 `CERTIFICATE` PEM instead of a `PUBLIC KEY` PEM A raw DER SPKI blob also bypasses the guard, but DER cannot be used for genuine asymmetric verification through this path, so I exclude it as a non-deployable configuration and mention it only as further evidence of the recogniser/loader gap. ## Details `authlib/jose/rfc7518/oct_key.py:10-17` ```python POSSIBLE_UNSAFE_KEYS = ( b"-----BEGIN ", b"---- BEGIN ", b"ssh-rsa ", b"ssh-dss ", b"ssh-ed25519 ", b"ecdsa-sha2-", ) ``` `authlib/jose/rfc7518/oct_key.py:78-84` ```python raw_key = to_bytes(raw) # security check if raw_key.startswith(POSSIBLE_UNSAFE_KEYS): raise ValueError("This key may not be safe to import") key = cls(raw_key=raw_key, options=options) ``` `bytes.startswith` is anchored. The loader on the asymmetric path is not: `authlib/jose/rfc7517/_cryptography_key.py:22-29` calls `load_pem_public_key` / `load_pem_x509_certificate`, both of which scan forward for the armour header. So the set of byte strings the recogniser rejects is strictly smaller than the set the loader accepts, and the difference is exploitable. Reach, for a caller of the documented object: * `authlib/jose/rfc7518/jws_algs.py:59` — `HMACAlgorithm.prepare_key(raw_data)` returns `OctKey.import_key(raw_data)`; this is the whole of the HMAC key handling. * `authlib/jose/rfc7519/jwt.py:80` — `JsonWebToken.decode(self, s, key, claims_cls=None, claims_options=None, claims_params=None)`; the algorithm comes from the token header and is checked only against the instance's list. * `authlib/jose/__init__.py:55-72` — that list contains both HS\* and RS\*/ES\*/PS\*/EdDSA. So `oct_key.py:81` is the only thing between a mixed allow-list and using the public key as an HMAC secret, and it is bypassed by a single leading byte. ## PoC `poc-authlib-e2e-http.py` stands up a Flask API whose only authentication is the documented `jwt.decode(token, PUBLIC_KEY)`
References
- github.com/authlib/authlib/security/advisories/GHSA-7vr2-2fgp-9w7p (opens in a new tab)
- nvd.nist.gov/vuln/detail/CVE-2026-114615 (opens in a new tab)
- github.com/authlib/authlib/pull/945 (opens in a new tab)
- github.com/authlib/authlib/commit/e3bd41cb29dcfee434453b135ffb487884baeedf (opens in a new tab)
- github.com/authlib/authlib (opens in a new tab)
- github.com/authlib/authlib/releases/tag/v1.9.0 (opens in a new tab)