Skip to main content
Vulnerability Database/CVE-2026-102273

CVE-2026-102273: PyJWT HMAC Authentication Bypass Vulnerability

CVE-2026-102273 is an authentication bypass flaw in PyJWT that allows attackers to forge tokens using public keys as HMAC secrets. This post explains its impact, affected versions, and mitigation steps.

Published:

CVE-2026-102273 Overview

CVE-2026-102273 is a signature verification flaw [CWE-347] in PyJWT, the Python implementation of the JSON Web Token (JWT) standard. Versions from 2.13.0 up to (but not including) 2.14.0 fail to reject public JSON Web Key (JWK) container representations when used as HMAC secrets. The HMACAlgorithm.prepare_key guard only inspects top-level public JWK forms and misses container representations. Applications that accept both HMAC and asymmetric algorithms can be tricked into using public asymmetric key material as an HMAC secret. An attacker who knows the public key can forge tokens with arbitrary authenticated claims.

Critical Impact

An attacker who obtains the public key can forge JWTs with arbitrary claims, enabling authentication bypass and privilege escalation in applications that mix HMAC and asymmetric algorithms.

Affected Products

  • PyJWT versions 2.13.0 through 2.13.x
  • Python applications using PyJWT that accept both HMAC and asymmetric algorithms
  • Services that pass public JWK containers as raw key material to jwt.decode()

Discovery Timeline

  • 2026-09-28 - CVE-2026-102273 published to NVD
  • 2026-09-30 - Last updated in NVD database

Technical Details for CVE-2026-102273

Vulnerability Analysis

The flaw is a classic JWT algorithm confusion condition rooted in incomplete input validation inside HMACAlgorithm.prepare_key. PyJWT contains a guard designed to reject public keys when they are passed as HMAC secrets. That guard recognizes top-level JWK forms such as raw JSON objects containing kty fields for RSA or EC keys. It does not, however, recognize JWK container representations, such as JWK Sets or wrapper objects that embed the key material one level deeper.

When an application accepts multiple algorithms (for example, ["HS256", "RS256"]) and passes a public JWK container as the raw key, PyJWT treats the container bytes as an HMAC shared secret. An attacker who knows the public key material can compute a valid HMAC over a forged header and payload, producing a token that verifies as authentic. This yields arbitrary claim forgery, including identity, roles, and scopes.

Root Cause

The root cause is incomplete key-shape detection in jwt/algorithms.py. The prepare_key guard parses the input as JSON and inspects only the top level for public JWK members. Container structures that nest the JWK inside another JSON object bypass the check. The parser also lacked hardening against pathological JSON structures that could trigger recursion errors and fall through the validation path.

Attack Vector

Exploitation requires that the target application both accepts HMAC algorithms alongside asymmetric algorithms in its algorithms allow-list and supplies a public JWK container to the decode function. The attacker forges a JWT with the alg header set to HS256 (or another HMAC variant) and signs it using the known public key bytes as the HMAC secret. The server-side PyJWT call then verifies the token successfully, granting the attacker whatever authorization the forged claims describe.

python
# Security patch in jwt/algorithms.py - reject public JWK container HMAC keys
         # should be loaded via PyJWK / from_jwk rather than fed as raw JSON
         # bytes (whose contents are not the secret material).
         try:
-            jwk_obj = json.loads(key_bytes)
+            jwk_obj = json.loads(key_bytes, parse_int=lambda _: 0)
         except RecursionError:
             try:
                 decoded_key = key_bytes.decode(
                     json.detect_encoding(key_bytes), errors="surrogatepass"
                 )
             except UnicodeError:
                 decoded_key = ""
-            if decoded_key.lstrip().startswith("{"):
+            stripped_key = decoded_key.lstrip("\\ufeff \t\r\n")
+            has_jwk_member = False
+            index = 0
+            while index < len(decoded_key):
+                if decoded_key[index] != '"':
+                    index += 1
+                    continue
+                end = index + 1
+                while end < len(decoded_key):
+                    if decoded_key[end] == "\\":
+                        end += 2
+                    elif decoded_key[end] == '"':
+                        break
+                    else:
+                        end += 1
+                if end >= len(decoded_key):
+                    break

Source: PyJWT commit 801cd12. The patch hardens JSON parsing against recursion errors and adds character-level scanning to detect JWK members within container representations, ensuring public key material is rejected before HMAC use.

Detection Methods for CVE-2026-102273

Indicators of Compromise

  • JWT tokens with alg header value of HS256, HS384, or HS512 arriving at endpoints that normally issue RS/ES-signed tokens.
  • Successful authentication events where the token's kid or iss corresponds to an asymmetric key pair but the algorithm is HMAC.
  • Anomalous claim values (elevated role, scope, sub) in tokens with valid signatures but no corresponding issuance record.

Detection Strategies

  • Inventory PyJWT dependencies across services and flag versions 2.13.0 through 2.13.x using SCA tools or pip list.
  • Statically scan application code for jwt.decode calls that pass a variable algorithms list containing both HS* and RS*/ES* values.
  • Audit key-loading code paths for cases where public JWK container objects are serialized and passed to jwt.decode as the key argument.

Monitoring Recommendations

  • Log every JWT verification with the algorithm used and the key identifier, and alert on mismatches against expected issuer policy.
  • Correlate authentication events with issuance records from identity providers to surface tokens minted outside the trusted issuer.
  • Monitor application error logs for InvalidKeyError and related PyJWT exceptions that may indicate probing activity.

How to Mitigate CVE-2026-102273

Immediate Actions Required

  • Upgrade PyJWT to version 2.14.0 or later in all Python services and rebuild container images.
  • Restrict the algorithms parameter passed to jwt.decode() to a single algorithm family that matches the key type in use.
  • Rotate any asymmetric key pairs whose public component may have been used as an HMAC secret input in vulnerable deployments.

Patch Information

The fix is available in PyJWT release 2.14.0. Details are published in GitHub Security Advisory GHSA-w2cx-738m-mc7w. Upgrade with pip install --upgrade "pyjwt>=2.14.0" and pin the minimum version in requirements.txt or pyproject.toml.

Workarounds

  • Never mix HMAC and asymmetric algorithms in the same algorithms allow-list passed to jwt.decode().
  • Load JWK material through PyJWK.from_jwk() rather than passing raw JSON bytes as the key argument.
  • Enforce strict alg header validation at the application layer before calling jwt.decode() and reject unexpected algorithm values.
bash
# Upgrade PyJWT and pin the safe minimum version
pip install --upgrade "pyjwt>=2.14.0"

# Verify installed version
python -c "import jwt; print(jwt.__version__)"

# Recommended decode pattern - restrict to a single algorithm family
# jwt.decode(token, key=public_key, algorithms=["RS256"])

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.