Sep 30, 2026·8 min read·1 visit
PyJWT versions 2.13.0 to 2.14.0 fail to validate key lengths on PyJWK paths, allowing attackers to forge tokens with an empty HMAC key if a trusted JWK contains an empty 'k' parameter.
A signature verification bypass vulnerability in PyJWT allows unauthenticated remote attackers to forge JSON Web Tokens when processing JSON Web Key Sets containing an empty symmetric key.
CVE-2026-102266 represents a signature verification bypass vulnerability in PyJWT, a prominent Python library implementing JSON Web Token (JWT) and JSON Web Signature (JWS) standards. The flaw specifically affects PyJWT versions 2.13.0 up to, but not including, 2.14.0. The bug resides within the signature validation pathway for tokens verified using JSON Web Keys (JWKs) or JSON Web Key Sets (JWKS). Under specific configurations, this allows an unauthenticated remote attacker to bypass cryptographic trust controls and forge arbitrary JWT assertions.
The failure is located in the key handling discrepancy between raw cryptographic keys and keys encapsulated within a PyJWK object. When PyJWT decodes a token using a raw key, it executes input validation checks to prevent the use of weak or empty keys. However, when parsing and processing keys extracted from a PyJWK instance, the verification pipeline bypassed these validation steps. This architectural oversight allowed the verification process to execute using an empty byte-string secret key when a trusted JWK structure contained empty material.
Because the library defaulted to raising warnings instead of exceptions for inadequate key lengths, the verification function proceeded to calculate and validate signatures using an empty HMAC key. Consequently, if an application relies on a JWK Set containing a symmetric key with an empty representation, an offline attacker can construct a valid signature using a null-byte key. This results in complete validation bypass, granting the attacker the ability to inject arbitrary claims and assume unauthorized identities within the application context.
The root cause of CVE-2026-102266 lies in the dual-path design of the key validation logic inside PyJWT. Historically, PyJWT implemented a validation pipeline for raw cryptographic keys through the prepare_key method of individual algorithm classes. For HMAC algorithms, this method verified that the key length was non-zero and threw an InvalidKeyError if an empty key was provided. This safe-default behavior ensured that any attempt to initialize an HMAC verification operation with a blank key would fail immediately prior to execution.
In contrast, when the verification pipeline utilized a PyJWK key representation (often dynamically resolved from JWK Sets), the code bypassed this central validation checkpoint. Instead of invoking the algorithm's specialized prepare_key method, the JWS verification layer directly retrieved the raw internal key buffer from the PyJWK object. If the source JWK entry was a symmetric oct key with an empty k parameter, the Base64URL decoding mechanism parsed this parameter into an empty byte string b"" without triggering any initialization errors.
This dual-path vulnerability was compounded by the default handling of key length violations within PyJWT. When the enforce_minimum_key_length configuration option is disabled, which is the default setting, the library only emits an InsecureKeyLengthWarning instead of raising an exception when encountering extremely short keys. Because the empty key path did not trigger an upfront validation failure, the verification function continued its execution path, comparing the incoming signature against an HMAC value generated with a zero-length secret key.
To understand the mechanics of the vulnerability, we analyze the key loading and signature validation files. The code block below contrasts the vulnerable key loading path in jwt/api_jws.py with the corrected implementation of version 2.14.0. Prior to the fix, the validation logic directly extracted the internal raw key representation from the PyJWK object without invoking the algorithm-specific key preparation logic.
# Vulnerable code in PyJWS._verify_signature (jwt/api_jws.py)
alg_obj = key.Algorithm
prepared_key = key.key # Bypasses prepare_key validation entirely
# Patched code in PyJWS._verify_signature (jwt/api_jws.py)
alg_obj = key.Algorithm
prepared_key = alg_obj.prepare_key(key.key) # Core Fix: apply prepare_key checksBy replacing key.key with alg_obj.prepare_key(key.key), the patch ensures that any key resolved from a PyJWK context is forced through the same validation checks as a raw key, causing the library to throw an InvalidKeyError if the key length is zero.
The secondary patch acts as a defense-in-depth measure within the JWK parser in jwt/algorithms.py. When an HMAC key is loaded via the from_jwk interface, the updated logic validates the length of the decoded key material before creating the internal representation. If the decoded byte sequence is empty, the parser raises an InvalidKeyError during key initialization, preventing the creation of a vulnerable PyJWK instance entirely.
# Patched code in HMACAlgorithm.from_jwk (jwt/algorithms.py)
key_bytes = base64url_decode(obj["k"])
if len(key_bytes) == 0:
raise InvalidKeyError("HMAC key must not be empty.") # Prevent loading empty oct keys
return key_bytesExploitation of CVE-2026-102266 requires specific environmental criteria but presents a low attack complexity once those criteria are met. The target application must process token signatures using keys retrieved from a JSON Web Key Set (JWKS). Crucially, the JWKS must contain an oct key with an empty key parameter "k": "". This condition often arises in testing environments, misconfigured directory services, or situations where keys are populated from uninitialized databases.
Once an empty oct key is present in the active JWKS, the attacker can execute the exploit completely offline. The attacker constructs a JWT payload with administrative privileges or desired claims, referencing the identifier (kid) of the empty key in the header. The attacker then generates an HMAC-SHA256 signature using a zero-length byte string (b"") as the secret key. The resulting base64url-encoded signature is appended to the token header and payload.
The following test script demonstrates how the signature generation is constructed and demonstrates how vulnerable versions of PyJWT accept the forged token, while patched versions throw an exception:
import base64
import hashlib
import hmac
import jwt
def test_exploit():
empty_jwk = {
"kty": "oct",
"k": "",
"kid": "active",
"alg": "HS256"
}
jwk_obj = jwt.PyJWK.from_dict(empty_jwk)
signing_input = b"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdHRhY2tlciJ9"
signature_bytes = hmac.new(b"", signing_input, hashlib.sha256).digest()
encoded_signature = base64.urlsafe_b64encode(signature_bytes).rstrip(b"=")
token = (signing_input + b"." + encoded_signature).decode()
try:
decoded = jwt.decode(token, jwk_obj, algorithms=["HS256"])
print("Verification Succeeded: " + str(decoded))
except jwt.InvalidKeyError as e:
print("Verification Blocked: " + str(e))The impact of CVE-2026-102266 is high because it allows complete authentication and authorization bypass. An attacker who successfully exploits this vulnerability can forge valid cryptographic signatures representing any user identity, including administrative accounts. In typical deployments where JWTs are used to transmit authorization claims, this enables unauthorized privilege escalation, complete data access, and administrative control over the affected application.
The National Vulnerability Database assigns this vulnerability a CVSS score of 7.4. The vector string is CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N. The attack vector is Network, indicating remote exploitation capability. The complexity is High because of the prerequisite that the trust store contains an empty key. Confidentially and integrity impacts are both rated High, as arbitrary operations can be performed on behalf of simulated user sessions.
Although no active in-the-wild exploitation has been recorded by CISA or threat intelligence networks, the availability of clear proof-of-concept material increases the threat profile. Software developers and operations teams must prioritize remediation, particularly in multi-tenant environments where JWK Sets are dynamically fetched or managed by tenant-controlled identity providers.
The primary remediation strategy is upgrading the PyJWT library to version 2.14.0 or higher. This version integrates the dual fixes that enforce strict key length validations on the PyJWK verification path and block empty HMAC keys during dynamic JWK parsing. The update is backwards-compatible and can be applied through python package managers using standard commands such as pip install --upgrade pyjwt.
When immediate upgrading is not feasible, several defensive workarounds can be applied at the application or gateway layer. Security teams should enforce key length validations by explicitly passing the enforce_minimum_key_length configuration option as True during token decoding. This ensures that even if an empty key bypasses the initial check, the signature validation phase raises an error due to the insufficient key length.
Additionally, applications that ingest remote JWK Sets should sanitize incoming keys. The sanitization logic must inspect the key list and explicitly reject any oct key where the k parameter is absent, null, or is an empty string. This preventative step blocks the entry of the vulnerable condition into the active key memory, mitigating the root cause of the signature bypass.
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N| Attribute | Detail |
|---|---|
| CWE ID | CWE-347 |
| Attack Vector | Network (AV:N) |
| CVSS | 7.4 |
| EPSS | 0.00175 |
| Impact | Authentication Bypass / Privilege Escalation |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
The software imports or uses a cryptographic key or signature but does not verify or improperly verifies the signature, allowing attackers to bypass authentication or integrity checks.
A Regular Expression Denial of Service (ReDoS) vulnerability exists in Nodemailer's addressparser fallback engine before version 10.0.6. Under specific malformed inputs with excessive word boundaries, the parser exhibits quadratic backtracking, leading to high CPU utilization and event loop blockage.
Nodemailer versions prior to 10.0.9 are vulnerable to a parser differential bug. When processing a quoted local-part followed by an RFC 5322 comment and trailing characters, the internal addressparser module fails to order its normalization routine correctly. This error results in the generation of malformed envelope recipient addresses containing injected whitespace and secondary domains, allowing attackers to bypass routing restrictions and exfiltrate sensitive emails.
CVE-2026-102276 is a high-severity Denial of Service (DoS) vulnerability impacting the 'brace-expansion' library, a popular Node.js utility designed to expand brace patterns into combinatorial lists. Due to uncontrolled recursion and argument-list stack exhaustion within the internal parseCommaParts function, remote attackers can trigger an unhandled RangeError that abruptly terminates the Node.js process.
A stack-based Denial of Service (DoS) vulnerability via uncontrolled recursion in the brace-expansion library prior to versions 1.1.20, 2.1.6, 3.0.8, and 5.0.11 allows unauthenticated remote attackers to trigger native stack exhaustion, terminating the Node.js process via a crafted payload containing deeply nested brace groups.
An uncontrolled resource consumption vulnerability exists in the brace-expansion JavaScript library. Due to an algorithmic flaw in parsing a legacy Bash-compatibility quirk involving {a},b}-shaped expansion structures, untrusted inputs containing many trailing closing braces trigger successive full-input rescans. This behavior yields quadratic CPU time complexity and high memory overhead, allowing remote, unauthenticated attackers to cause a Denial of Service (DoS) by blocking the single-threaded Node.js event loop.
Moment.js versions 2.29.2 through 2.30.1 are vulnerable to a Path Traversal flaw (CWE-27) on server-side Node.js environments when dynamic locales are configured. The vulnerability stems from an object-coercion bypass in the locale-name sanitization routine, which assumes incoming variables are string primitives. An attacker can pass a structured object with custom 'match' and 'toString' properties to bypass regex-based directory checks, leading to arbitrary file loading via Node's internal 'require()' call.