GHSA-752w-5fwx-jx9f CVE-2026-32597 PYSEC-2026-120
PyJWT accepts unknown `crit` header extensions
- Severity
- 7.5 / 10
- High · CVSS 3.1
- Exploit chance (EPSS)
- 0.28%
- Chance of exploitation in the next 30 days. Higher than 20.45% of scored vulnerabilities.
- Known exploited
- No
- Not in CISA's catalog
- Fix
2.12.0- Upgrade pyjwt to this version or later
Description
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):
return base64.urlsafe_b64encode(data).rstrip(b"=").decode()
h = b64url(json.dumps(header, separators=(",", ":")).encode())
p = b64url(json.dumps(payload, separators=(",", ":")).encode())
sig = b64url(hmac.new(b"secret", f"{h}.{p}".encode(), hashlib.sha256).digest())
token = f"{h}.{p}.{sig}"
# Should REJECT — x-custom-policy is not understood by PyJWT
try:
result = jwt.decode(token, "secret", algorithms=["HS256"])
print(f"ACCEPTED: {result}")
# Output: ACCEPTED: {'sub': 'attacker', 'role': 'admin'}
except Exception as e:
print(f"REJECTED: {e}")
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
critcarries 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")
for ext in crit:
if ext not in self._SUPPORTED_CRIT:
raise InvalidTokenError(f"Unsupported critical extension: {ext}")
if ext not in headers:
raise InvalidTokenError(f"Critical extension {ext} not in header")
CWE
- CWE-345: Insufficient Verification of Data Authenticity
- CWE-863: Incorrect Authorization
References
Affected versions
PyPI
| Package | Vulnerable | Fixed in |
|---|---|---|
| pyjwt | < 2.12.0 |
2.12.0 |
How to fix
Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.
pyjwt PyPI → 2.12.0
- pip
pip install --upgrade "pyjwt>=2.12.0"- poetry
poetry add "pyjwt>=2.12.0"- uv
uv add "pyjwt>=2.12.0"
Severity breakdown
CVSS 3.1 7.5
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N
- Attack vector
- Network
- Attack complexity
- Low
- Privileges required
- None
- User interaction
- None
- Scope
- Unchanged
- Confidentiality
- None
- Integrity
- High
- Availability
- None
Weakness
CWE-345: Insufficient Verification of Data Authenticity
The product does not sufficiently verify the origin or authenticity of data, in a way that causes it to accept invalid data.
CWE-863: Incorrect Authorization
The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check.
How to prevent it
- Architecture and Design: Divide the product into anonymous, normal, privileged, and administrative areas. Reduce the attack surface by carefully mapping roles with data and functionality. Use role-based access control (RBAC) [REF-229] to enforce the roles at the appropriate boundaries. Note that this approach may not protect against horizontal authorization, i.e., it will not protect a user from attacking others with the same role.
- Architecture and Design: Ensure that access control checks are performed related to the business logic. These checks may be different than the access control checks that are applied to more generic resources such as files, connections, processes, memory, and database records. For example, a database may restrict access for medical records to a specific database user, but each record might only be intended to be accessible to the patient and the patient's doctor [REF-7].
- Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid. For example, consider using authorization frameworks such as the JAAS Authorization Framework [REF-233] and the OWASP ESAPI Access Control feature [REF-45].
- Architecture and Design: For web applications, make sure that the access control mechanism is enforced correctly at the server side on every page. Users should not be able to access any unauthorized functionality or information by simply requesting direct access to that page. One way to do this is to ensure that all pages containing sensitive information are not cached, and that all such pages restrict access to requests that are accompanied by an active and authenticated session token associated with a user who has the required permissions to access that page.
- System Configuration, Installation: Use the access control capabilities of your operating system and server environment and define your access control lists accordingly. Use a "default deny" policy when defining these ACLs.
References
Advisories
Packages
Other links
- github.com/jpadilla/pyjwt/security/advisories/GHSA-752w-5fwx-jx9f
- github.com/pypa/advisory-database/tree/main/vulns/pyjwt/PYSEC-2026-120.yaml
- lists.debian.org/debian-lts-announce/2026/05/msg00008.html
- access.redhat.com/errata/RHSA-2026:10140
- access.redhat.com/errata/RHSA-2026:10141
- access.redhat.com/errata/RHSA-2026:10184
- access.redhat.com/errata/RHSA-2026:12176
- access.redhat.com/errata/RHSA-2026:13508
- access.redhat.com/errata/RHSA-2026:13512
- access.redhat.com/errata/RHSA-2026:13545
- access.redhat.com/errata/RHSA-2026:13553
- access.redhat.com/errata/RHSA-2026:13672
- access.redhat.com/errata/RHSA-2026:13916
- access.redhat.com/errata/RHSA-2026:17083
- access.redhat.com/errata/RHSA-2026:19138
- access.redhat.com/errata/RHSA-2026:19355
- access.redhat.com/errata/RHSA-2026:19375
- access.redhat.com/errata/RHSA-2026:19712
- access.redhat.com/errata/RHSA-2026:21431
- access.redhat.com/errata/RHSA-2026:21517
- access.redhat.com/errata/RHSA-2026:22330
- access.redhat.com/errata/RHSA-2026:24977
- access.redhat.com/errata/RHSA-2026:26226
- access.redhat.com/errata/RHSA-2026:37275
- access.redhat.com/errata/RHSA-2026:42644
- access.redhat.com/errata/RHSA-2026:6568
- access.redhat.com/errata/RHSA-2026:6720
- access.redhat.com/errata/RHSA-2026:6912
- access.redhat.com/errata/RHSA-2026:6926
- access.redhat.com/errata/RHSA-2026:8437
- access.redhat.com/errata/RHSA-2026:8746
- access.redhat.com/errata/RHSA-2026:8747
- access.redhat.com/errata/RHSA-2026:8748
- access.redhat.com/security/cve/CVE-2026-32597
- bugzilla.redhat.com/show_bug.cgi
- security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-32597.json
Timeline
- Published
- Last updated by the source
- Latest EPSS score
Related advisories
| Advisory | Severity | Why it's related |
|---|---|---|
| GHSA-pxh4-856f-4h89 PyJWT: HS256/384/512 verify accepts empty HMAC key via oct JWK (PyJWK/PyJWKClient) enabling token forgery | High | Also affects pyjwt |
| GHSA-gvp8-978c-rx2q PyJWT.decode() reintroduces options-dict mutation, enabling silent claim-verification bypass on dict reuse | Medium | Also affects pyjwt |
| GHSA-x33g-cr3x-6449 PyJWT accepts inconsistent OKP x/d JWKs, causing public/private key identity confusion | High | Also affects pyjwt |
| GHSA-w6j9-cwv2-h6wq PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set | High | Also affects pyjwt |
| GHSA-w2cx-738m-mc7w PyJWT accepts public JWK containers as HMAC secrets | High | Also affects pyjwt |
| GHSA-r6x4-923q-g947 PyJWT BOM Bypass | High | Also affects pyjwt |
| GHSA-p4g4-x82p-q773 PyJWT: Public keys in DER form are accepted as HMAC secrets, bypassing the CVE-2022-29217 guard | High | Also affects pyjwt |
| GHSA-jwrc-g2q2-pq5p PyJWT: ReDoS vulnerability when calling the `is_pem_format` function. | Medium | Also affects pyjwt |
MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.
Check your repos with MergeHarbor