ZeroPath at Black Hat USA 2026

PyJWT CVE-2026-48526: Brief Summary of JWK Algorithm Confusion Leading to Token Forgery

A brief summary of CVE-2026-48526, a high severity algorithm confusion vulnerability in PyJWT that allows attackers to forge JWT tokens by exploiting a gap in HMAC key validation for JWK format keys. Includes patch details and mitigation guidance.

CVE Analysis

10 min read

ZeroPath CVE Analysis
ZeroPath CVE Analysis

2026-05-28

PyJWT CVE-2026-48526: Brief Summary of JWK Algorithm Confusion Leading to Token Forgery
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A partial security fix applied four years ago to PyJWT, the most popular JWT library in the Python ecosystem, left a gap that allows attackers to forge arbitrary authentication tokens by exploiting a different key format than the one originally blocked. CVE-2026-48526 is the result: a JWK format bypass of the algorithm confusion protections introduced in PyJWT 2.4.0, carrying a CVSS v3.1 score of 7.4 and enabling full authentication bypass when specific preconditions are met.

PyJWT implements RFC 7519 (JSON Web Token) for Python and is maintained by José Padilla. With over 5,700 GitHub stars and deep integration across Django, Flask, FastAPI, and Auth0's own Python SDK, it is the de facto standard for JWT handling in the Python ecosystem. Any vulnerability in PyJWT carries a substantial blast radius across Python web applications that rely on token based authentication.

Technical Information

Root Cause: JWK JSON Bypasses HMAC Key Validation

The vulnerability lives in the HMACAlgorithm.prepare_key method within jwt/algorithms.py. This method is the gatekeeper for key material used in HMAC signing and verification operations. When CVE-2022-29217 was fixed in PyJWT 2.4.0, the method was updated to detect and reject PEM formatted and SSH formatted public keys, raising an InvalidKeyError if either format was detected. The logic was straightforward: if someone passes an asymmetric public key where an HMAC secret is expected, something has gone wrong.

However, the fix used a blocklist approach, explicitly checking for known dangerous formats rather than whitelisting acceptable ones. JSON Web Key (JWK) format was not included in the blocklist. The vulnerable code pattern in versions up through 2.10.1 looked like this:

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(...) return key_bytes

A JWK JSON string, such as {"kty":"RSA","n":"...","e":"AQAB"}, passes straight through both checks. Since HMAC algorithms accept arbitrary byte strings as secrets, the raw text of the JWK document is silently accepted as a valid HMAC key.

Attack Prerequisites

Exploitation requires two conditions to be simultaneously true in the target application:

ConditionDescription
Mixed algorithm familiesThe verifier passes both a symmetric algorithm (e.g., HS256) and an asymmetric algorithm (e.g., RS256) in the algorithms list when calling decode()
Raw JWK as key argumentThe verifier supplies the public key as a raw JWK JSON string via the key parameter instead of using a PEM encoded key or a dedicated key object

The CVSS vector reflects this: Attack Complexity is rated High (AC:H) because both conditions must hold. But when they do, the Confidentiality and Integrity impacts are both High, meaning the attacker achieves full token forgery.

Attack Flow

The attack follows the classic JWT algorithm confusion pattern, adapted for the JWK format gap:

  1. Obtain the public key in JWK format. The attacker retrieves the issuer's public key from a publicly accessible endpoint such as .well-known/jwks.json. This key is public by design and freely available.

  2. Craft a malicious token header. The attacker creates a JWT with the header {"alg":"HS256","typ":"JWT"}, switching the algorithm from the expected asymmetric algorithm (e.g., RS256) to HMAC.

  3. Set arbitrary claims. The attacker constructs any payload they wish, such as {"sub":"alice","admin":true}, to impersonate any identity or assume any role.

  4. Sign with HMAC using the JWK JSON as the secret. The attacker computes the HMAC SHA256 signature using the raw JWK JSON text as the symmetric secret key. Since the verifier will use the same JWK text as its "key" argument, the signatures will match.

  5. Submit the forged token. When the vulnerable verifier processes this token with a call like decode(token, key=rsa_jwk_json, algorithms=["HS256","RS256"]), it sees the alg header is HS256, routes verification to HMACAlgorithm, passes the JWK string through prepare_key (which does not reject it), and validates the attacker's forged signature as legitimate.

The result is a complete authentication bypass. The attacker can impersonate any user, escalate privileges, or access any protected resource that trusts the JWT verifier.

Relationship to CVE-2022-29217

This vulnerability is a direct descendant of CVE-2022-29217 (GHSA-ffqj-6fqr-9h24), which was fixed in PyJWT 2.4.0. That earlier fix addressed the same algorithm confusion attack class but only for PEM and SSH key formats. The comparison is instructive:

AttributeCVE-2022-29217CVE-2026-48526
AdvisoryGHSA-ffqj-6fqr-9h24GHSA-xgmm-8j9v-c9wx
Key format bypassedPEM, SSHJWK JSON
Fixed version2.4.02.13.0
CVSS7.57.4
Root causeHMACAlgorithm.prepare_key insufficient validationSame class, different format gap

Both vulnerabilities share the same fundamental root cause: HMACAlgorithm.prepare_key relied on an enumerated blocklist of key formats rather than a whitelist of acceptable HMAC key types. Any format not explicitly blocked became an attack vector. The JWK gap persisted across every release from 2.4.0 through 2.10.1, meaning applications that patched CVE-2022-29217 remained vulnerable to this variant for over two years.

CVSS v3.1 Vector Breakdown

The full vector is CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N, which breaks down as follows:

MetricValueRationale
Attack VectorNetwork (AV:N)Exploitable over the network via crafted JWT
Attack ComplexityHigh (AC:H)Requires mixed algorithm families and JWK key format
Privileges RequiredNone (PR:N)No authentication needed
User InteractionNone (UI:N)No user action required
ScopeUnchanged (S:U)Impact confined to the vulnerable component
ConfidentialityHigh (C:H)Full access to protected data
IntegrityHigh (I:H)Token forgery enables arbitrary identity impersonation
AvailabilityNone (A:N)No direct availability impact

The NVD assigns both CWE-287 (Improper Authentication) and CWE-347 (Improper Verification of Cryptographic Signature), reflecting that this is simultaneously an authentication logic error and a cryptographic verification failure.

Patch Information

A public patch for CVE-2026-48526 is available in PyJWT version 2.13.0, released on May 21, 2026. The fix was committed by maintainer José Padilla in commit 95791b1 and tagged in 7144e45, tracked under GitHub Security Advisory GHSA-xgmm-8j9v-c9wx.

The patch adds a new guard in HMACAlgorithm.prepare_key after the existing PEM/SSH checks. It strips leading whitespace from the key bytes, checks if the result starts with {, and if so, attempts to parse it as JSON. If parsing succeeds and the resulting dictionary contains a "kty" key (the mandatory JWK field per RFC 7517), the method raises an InvalidKeyError:

# Defense against algorithm-confusion attacks: an attacker with # control over the token header can force this code path by setting # alg=HS*, and HMACAlgorithm is the only algorithm that accepts # arbitrary bytes as a valid secret. stripped = key_bytes.lstrip() if stripped.startswith(b"{"): try: jwk_obj = json.loads(key_bytes) except ValueError: jwk_obj = None if isinstance(jwk_obj, dict) and "kty" in jwk_obj: raise InvalidKeyError( "The specified key looks like a JWK and should not be " "used directly as an HMAC secret. Load it via " "PyJWK / HMACAlgorithm.from_jwk first." )

This logic is deliberately narrow: JSON strings that do not contain a "kty" field are still accepted as HMAC secrets, because a JSON shaped string without JWK structure is a legitimate (if unusual) secret. This avoids false positives.

In addition to the JWK guard, the same commit also hardens prepare_key by outright rejecting empty HMAC keys (len(key_bytes) == 0) with an InvalidKeyError, rather than the previous behavior of merely emitting a warning. This catches the common misconfiguration of os.getenv("JWT_SECRET", "") falling through silently.

The fix was accompanied by comprehensive test coverage, including parametrized tests that feed RSA, EC, OKP, and HMAC JWK files into prepare_key and assert they are all rejected, as well as a negative test ensuring plain JSON strings without "kty" remain accepted.

To upgrade:

pip install --upgrade PyJWT==2.13.0

Version 2.13.0 also includes four additional security fixes that address related attack surfaces:

AdvisoryIssue
GHSA-jq35-7prp-9v3fAlgorithm allow list bypass via PyJWK/PyJWKClient
GHSA-993g-76c3-p5m4URI scheme injection in PyJWKClient
GHSA-fhv5-28vv-h8m8JWK Set cache cleared on transient fetch errors
GHSA-w7vc-732c-9m39Unauthenticated DoS via b64=false base64 decode

Organizations should upgrade to 2.13.0 not only for the CVE-2026-48526 fix but also to receive these complementary hardening measures.

Post Upgrade Verification Checklist

After upgrading, security teams should verify:

  1. All PyJWT dependencies are at version 2.13.0 or later.
  2. No decode() calls mix symmetric and asymmetric algorithm families.
  3. No decode() calls pass raw JWK JSON strings as the key argument.
  4. The algorithms parameter is always explicitly specified (never omitted or defaulted).

Affected Systems and Versions

The vulnerability affects PyJWT versions prior to 2.13.0. More specifically:

  • Versions 2.4.0 through 2.10.1 are vulnerable to the JWK format bypass described in CVE-2026-48526. These versions contain the CVE-2022-29217 fix (blocking PEM and SSH keys) but lack the JWK guard.
  • Versions prior to 2.4.0 are vulnerable to both the original PEM/SSH algorithm confusion (CVE-2022-29217) and the JWK variant.
  • The vulnerability is only exploitable when the application is configured to accept both symmetric and asymmetric algorithms in the algorithms list and passes a raw JWK JSON string as the key argument to decode().

The fixed version is PyJWT 2.13.0.

Vendor Security History

PyJWT has experienced multiple algorithm related vulnerabilities over its lifetime, revealing a persistent architectural challenge in safely handling the JWT specification's algorithm flexibility:

CVEYearSeverityIssueFixed Version
CVE-2022-2921720227.5 HighKey confusion via non blocklisted PEM/SSH key formats2.4.0
CVE-2025-457682025N/AWeak encryption vulnerability in PyJWT v2.10.1Under investigation
CVE-2026-325972026N/ARelated to GHSA-752w advisory2.13.0
CVE-2026-4852620267.4 HighJWK JSON accepted as HMAC secret (algorithm confusion)2.13.0

The evolution from CVE-2022-29217 to CVE-2026-48526 demonstrates a pattern: the blocklist approach to key format validation creates a "whack a mole" dynamic where each new key format representation becomes a potential bypass. The 2.13.0 release, which addresses five security advisories simultaneously, suggests the maintainer has shifted toward a more systemic security posture. The inclusion of algorithm binding (GHSA-jq35-7prp-9v3f) in the same release is particularly notable, as it moves toward a type aware verification model rather than relying solely on format detection.

A parallel vulnerability in the Node.js ecosystem, CVE-2026-34950 in the fast-jwt library, confirms that algorithm confusion remains an active and recurring challenge across JWT implementations in multiple languages.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Jira
  • Linear
  • Slack
  • Security Compass
Security magnifying glass visualization