Lack of data validation In pyjwt
Description
PyJWT accepts unknown crit header extensions
Summary
PyJWT does not validate the crit (Critical) Header Parameter defined in
RFC 7515 §4.1.11. When a JWS token contains a crit array listing
extensions that PyJWT does not understand, the library accepts the token
instead of rejecting it. This violates the MUST requirement in the RFC.
This is the same class of vulnerability as CVE-2025-59420 (Authlib), which received CVSS 7.5 (HIGH).
RFC Requirement
RFC 7515 §4.1.11:
The "crit" (Critical) Header Parameter indicates that extensions to this specification and/or [JWA] are being used that MUST be understood and processed. [...] If any of the listed extension Header Parameters are not understood and supported by the recipient, then the JWS is invalid.
Proof of Concept
import jwt # PyJWT 2.8.0 import hmac, hashlib, base64, json # Construct token with unknown critical extension header = {"alg": "HS256", "crit": ["x-custom-policy"], "x-custom-policy": "require-mfa"} payload = {"sub": "attacker", "role": "admin"} def b64url(data):...
Expected: jwt.exceptions.InvalidTokenError: Unsupported critical extension: x-custom-policy
Actual: Token accepted, payload returned.
Comparison with RFC-compliant library
# jwcrypto — correctly rejects from jwcrypto import jwt as jw_jwt, jwk key = jwk.JWK(kty="oct", k=b64url(b"secret")) jw_jwt.JWT(jwt=token, key=key, algs=["HS256"]) # raises: InvalidJWSObject('Unknown critical header: "x-custom-policy"')
Impact
Split-brain verification in mixed-library deployments (e.g., API gateway using jwcrypto rejects, backend using PyJWT accepts)
Security policy bypass when crit carries enforcement semantics
(MFA, token binding, scope restrictions)
Token binding bypass — RFC 7800 cnf (Proof-of-Possession) can be
silently ignored
See CVE-2025-59420 for full impact analysis
Suggested Fix
In jwt/api_jwt.py, add validation in _validate_headers() or
decode():
_SUPPORTED_CRIT = {"b64"} # Add extensions PyJWT actually supports def _validate_crit(self, headers: dict) -> None: crit = headers.get("crit") if crit is None: return if not isinstance(crit, list) or len(crit) == 0: raise InvalidTokenError("crit must be a non-empty array")...
CWE
CWE-345: Insufficient Verification of Data Authenticity
CWE-863: Incorrect Authorization
References
Mitigation
Update Impact
Minimal update. May introduce new vulnerabilities or breaking changes.
Ecosystem | Package | Affected version | Patched versions |
|---|---|---|---|
debian 12 | 2.6.0-1+deb12u1 | ||
debian 13 | 2.10.1-2+deb13u1 | ||
rpm rhel9 | 0:4.10.0-110.el9_8.2 | ||
rpm rhel10 | 0:4.16.0-21.el10_2.1 | ||
rpm rhel8 | 0:4.2.1-129.el8_10.25 | ||
pypi | 2.12.0 | ||
debian 11 | 1.7.1-2+deb11u1 | ||
debian 14 | 2.12.1-1 | ||
rpm rhel10.0 | 0:4.16.0-5.el10_0.9 | ||
rpm rhel9.6 | 0:4.10.0-86.el9_6.16 |
1-10 of 11
10
Aliases
References