CVE-2026-48523: PyJWT Algorithm Bypass in JWK Verification (2.9.0–2.12.1)
PyJWT, a widely-used Python library for handling JSON Web Tokens, has a flaw in how it validates token signatures when using JWK (JSON Web Key) objects. Between versions 2.9.0 and 2.12.1, the library checks that a token's advertised algorithm is in the caller's allow-list, but then ignores that check and uses a different algorithm bound to the JWK object for actual verification. This means an attacker with access to a registered private key can craft a token that claims to use an allowed algorithm in its header while being signed with a disallowed algorithm stored in the JWK—and the library will accept it. The fix is available in version 2.13.0.
Source data · NVD / CISA · public domain
- CVSS
- 3.1 · 5.4 MEDIUM · CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N
- Weaknesses (CWE)
- CWE-347
- Affected products
- 1 configuration(s)
- Published / Modified
- 2026-05-28 / 2026-06-17
NVD description (verbatim)
PyJWT is a JSON Web Token implementation in Python. From 2.9.0 to 2.12.1, there is a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow. This vulnerability is fixed in 2.13.0.
2 reference(s) · View on NVD →
SEC.co analysis · AI-assisted, reviewed against source
Technical summary
The vulnerability lies in a logic gap between algorithm validation and signature verification in PyJWT's jwt.decode() and jwt.decode_complete() functions when called with PyJWK objects. The library performs two distinct algorithm checks: it validates the 'alg' claim in the JWT header against the caller-supplied algorithms allow-list, which appears to implement proper access control. However, the actual cryptographic signature verification is performed using the algorithm bound to the PyJWK object itself, not the header algorithm. This creates a bypass: an attacker holding a registered private key can sign a token using any algorithm available in their JWK object (including disallowed algorithms), set the JWT header's 'alg' field to an allowed algorithm, and pass both the header validation and signature verification. The flaw is particularly impactful in the documented PyJWKClient.get_signing_key_from_jwt() workflow, which is designed to retrieve and validate signing keys from JWKS endpoints. The root cause is CWE-347 (Improper Verification of Cryptographic Signature), where the signature verification mechanism fails to authenticate the algorithm it applies.
Business impact
Organizations relying on PyJWT for token validation in authentication, authorization, or API security contexts face a privilege escalation and authentication bypass risk. If an attacker controls or compromises a registered JWK/JWKS private key, they can forge valid-appearing tokens that circumvent the application's algorithm restrictions. This is especially critical in microservices and OAuth/OIDC implementations where PyJWT validates tokens from trusted key sets. The breach could allow unauthorized access to protected resources, lateral movement within systems that trust these tokens, and potential elevation of privileges if tokens contain role or permission claims. The risk is amplified in systems that explicitly configure restrictive algorithm allow-lists—a common hardening practice—because the allow-list provides a false sense of security that is bypassed by this flaw.
Affected systems
Any application using PyJWT versions 2.9.0 through 2.12.1 is affected. The vulnerability is triggered specifically when jwt.decode() or jwt.decode_complete() are called with a PyJWK object as the key parameter. Applications that use PyJWKClient (the recommended client for JWKS endpoint integration) are directly impacted. Python environments across all operating systems are vulnerable if they run affected PyJWT versions. Verify your application's dependency manifest (requirements.txt, setup.py, poetry.lock, etc.) to identify affected versions. Applications using only string-based keys (raw key material rather than PyJWK objects) are not vulnerable.
Exploitability
Exploitation requires the attacker to possess or control a registered JWK/JWKS private key—that is, a key that is already trusted and published in the application's key set. The attack does not require interaction from users (UI:N) and can be executed over the network (AV:N) by any authenticated or semi-privileged principal (PR:L). No special timing or conditions are needed (AC:L). The CVSS score of 5.4 MEDIUM reflects that while the technical bar to exploitation is low once a key is compromised, the prerequisite of key compromise limits the immediate threat. However, in scenarios where an attacker has already obtained a signing key (e.g., through a developer system compromise or insider threat), the exploit is trivial and requires no additional vulnerabilities.
Remediation
Upgrade PyJWT to version 2.13.0 or later immediately. This version corrects the algorithm verification logic to ensure that signature verification uses the algorithm specified in the JWT header, not the algorithm bound to the JWK object. After upgrading, restart all applications and services that depend on PyJWT. Verify the upgrade in all environments: development, staging, and production. Additionally, conduct a key rotation audit: if you suspect any JWK/JWKS private keys may have been compromised, rotate them and re-issue any tokens signed with old keys. Review application logs and token audit trails for anomalies dating back to when your affected version was deployed.
Patch guidance
Upgrade PyJWT as follows: For pip-based projects, run 'pip install --upgrade pyjwt>=2.13.0'. For projects using requirements.txt, update the line to 'pyjwt>=2.13.0' and reinstall. For Poetry users, run 'poetry update pyjwt'. For Pipenv, run 'pipenv update pyjwt'. Verify the installation with 'python -c "import jwt; print(jwt.__version__)"' to confirm version 2.13.0 or higher is in use. Test your application's token validation flows in a staging environment before deploying to production. If you maintain a software bill of materials (SBOM), update PyJWT's entry accordingly.
Detection guidance
Check your environment for PyJWT versions 2.9.0–2.12.1 using dependency scanning tools (e.g., pip freeze, poetry show, Snyk, OWASP Dependency-Check). In your application code, search for jwt.decode() and jwt.decode_complete() calls that accept PyJWK or PyJWKClient objects; these are the vulnerable call patterns. If you have token validation logs or metrics, look for tokens with header algorithm claims that differ from your configured allow-list (though the bug masks this at validation time). Network-based detection is difficult without code inspection, as the vulnerability produces no obvious network signatures. Focus on inventory and code review rather than network detection.
Why prioritize this
This vulnerability should be prioritized for immediate patching in any environment where PyJWT versions 2.9.0–2.12.1 are in use, particularly if those environments handle authentication or authorization tokens. Although the CVSS score is MEDIUM and KEV status is not published, the combination of high practical impact (authentication bypass), low exploitation complexity, and ease of remediation (a simple version bump) makes this a high-priority update. Organizations that have explicitly configured restrictive algorithm allow-lists should prioritize even more heavily, as the flaw directly undermines that security control. The window between the vulnerable versions (introduced in 2.9.0 and fixed in 2.13.0) is approximately 4 versions, suggesting the issue may have been in the wild for months.
Risk score, explained
The CVSS 5.4 MEDIUM score reflects a network-accessible vulnerability (AV:N) with low attack complexity (AC:L) that requires low privileges (PR:L) but delivers only partial confidentiality and integrity impact (C:L/I:L/A:N). However, this score may understate the practical risk in certain contexts. If a JWK/JWKS private key is compromised—a plausible scenario in supply-chain, insider threat, or developer workstation compromise incidents—an attacker gains a trivial method to forge tokens that bypass security controls. The requirement for authentication (PR:L) is a mitigating factor because it implies the attacker already has some level of access; however, in federated or OAuth scenarios, 'authentication' may include external parties or compromised third-party services. Organizations should assess their own risk tolerance based on the sensitivity of what tokens protect and the likelihood of key compromise in their threat model.
Frequently asked questions
Do I need to upgrade if I'm using PyJWT to sign tokens, not verify them?
If your application uses PyJWT only to create/sign tokens (jwt.encode()) and does not call jwt.decode() or jwt.decode_complete(), you are not vulnerable to this flaw. However, applications that verify tokens signed by other parties—or that both sign and verify—must upgrade immediately. Additionally, best practice recommends keeping all dependencies patched regardless, so upgrading is still recommended.
What is a PyJWK object and how does it differ from a regular key string?
A PyJWK object is a deserialized representation of a JSON Web Key (JWK), extracted from a JWK Set (JWKS) and bound to a specific algorithm at deserialization time. When you call jwt.decode() with a raw key string (like an RSA public key in PEM format), the algorithm is taken from the JWT header. When you use PyJWK, the algorithm is embedded in the object itself. The vulnerability arises because PyJWT's code path for PyJWK objects ignores the header algorithm and uses the embedded algorithm instead, bypassing the allow-list.
My application uses PyJWKClient to fetch keys from an OAuth/OIDC provider. Am I affected?
Yes. PyJWKClient.get_signing_key_from_jwt() is the documented use case for this vulnerability. If you are using PyJWKClient in versions 2.9.0–2.12.1, you must upgrade to 2.13.0. The good news is that the fix is fully backward compatible and requires no code changes on your side—only a version bump.
Could an attacker exploit this without having a registered private key?
No. The vulnerability requires the attacker to possess a private key that is already registered and trusted in the target application's JWK/JWKS set. If no such key is compromised or controlled by the attacker, this vulnerability cannot be exploited. This is why the PR:L (Privilege/Prerequisite: Low) component of the CVSS score requires some level of existing access or compromise.
This analysis is provided for informational purposes and reflects the CVE description and CVSS score as published. Organizations should verify their specific deployment context, including Python version, application architecture, and key management practices, when assessing risk. Patch testing should be performed in a staging environment before production deployment. This document does not constitute legal, compliance, or risk management advice. Refer to vendor advisories and your organization's change management process for authoritative guidance on patching timelines and procedures. Source: NVD (public-domain), retrieved 2026-07-07. Analysis generated by SEC.co (claude-haiku-4-5).
Related vulnerabilities
- CVE-2026-48526HIGHPyJWT Authentication Bypass via HMAC Algorithm Misuse
- CVE-2026-45614MEDIUMOP-TEE ECDH Private Key Recovery via Curve Validation Bypass
- CVE-2026-47201HIGHauthentik SAML XML Signature Wrapping Authentication Bypass
- CVE-2026-48522MEDIUMPyJWT SSRF via Unvalidated URL Schemes in JWKS Fetching
- CVE-2026-48525MEDIUMPyJWT Detached JWS DoS Vulnerability – Patch Guidance
- CVE-2026-48524LOWPyJWT Denial of Service via Unlimited JWKS Requests
- CVE-2018-25384MEDIUMStored XSS in Wikidforum 2.20 Allows Authenticated Attackers to Inject Malicious Scripts
- CVE-2018-25387MEDIUMHaPe PKH 1.1 Cross-Site Request Forgery (CSRF) Admin Password Reset