How Authentication Bypass happens in PyJWT and how to fix it
Introduction
In production Python applications using JWT-based authentication, a critical vulnerability lurked in PyJWT versions 2.12.1 and earlier. The vulnerability, tracked as CVE-2026-48526, allowed attackers to forge valid JSON Web Tokens by exploiting insufficient validation in the token verification process. This wasn't a minor issue—it was a complete authentication bypass that could allow an attacker to impersonate any user in the system.
The flaw existed in how PyJWT 2.12.1 handled token validation, particularly in the cryptographic signature verification logic. When applications relied on this library to validate JWT tokens from API requests or authentication flows, the weakened validation meant that specially crafted tokens could pass verification checks that should have rejected them. An attacker didn't need to steal a valid token or compromise a signing key; they could simply create a forged token that would be accepted by vulnerable applications.
The Vulnerability Explained
What is JWT Token Forgery?
JSON Web Tokens (JWTs) are a standard mechanism for stateless authentication in modern applications. A JWT consists of three parts separated by dots:
header.payload.signature
The signature is cryptographically generated using a secret key and ensures that:
1. The token hasn't been tampered with
2. The token was created by a trusted authority
3. The claims inside the token are authentic
A properly functioning JWT library should verify this signature before accepting any token. If the signature verification is weak or incomplete, an attacker can modify the token's payload (changing user IDs, permissions, etc.) and either:
- Create a new valid-looking signature, or
- Bypass the signature check entirely
The Flaw in PyJWT 2.12.1
The vulnerability in PyJWT 2.12.1 stemmed from insufficient validation in the token verification process. While the exact implementation details are protected until full disclosure, the vulnerability allowed attackers to:
- Manipulate token claims - Change the
sub(subject),iat(issued at),exp(expiration), or other critical claims - Bypass algorithm verification - Potentially force the library to accept tokens with unexpected algorithms
- Forge signatures - Create tokens that would pass validation checks
Consider a typical vulnerable usage pattern:
# Vulnerable code using PyJWT 2.12.1
import jwt
def verify_token(token, secret_key):
try:
payload = jwt.decode(token, secret_key, algorithms=["HS256"])
return payload
except jwt.InvalidTokenError:
return None
# An attacker could craft a malicious token that passes this check
# even though it was never signed with the legitimate secret key
The problem wasn't necessarily in the application code above, but in how PyJWT 2.12.1's decode() function validated the signature internally. The library had gaps in its validation logic that allowed certain malformed or specially crafted tokens to bypass security checks.
Real-World Attack Scenario
Imagine a web application using JWT for API authentication:
- Legitimate flow: User logs in, receives a JWT token with
{"user_id": 123, "role": "user"} - Attack with PyJWT 2.12.1: Attacker crafts a forged token with
{"user_id": 1, "role": "admin"}(changing the user ID to an admin account) - Vulnerable verification: The application calls
jwt.decode(forged_token, secret_key)using PyJWT 2.12.1 - Bypass: Due to the validation flaw, PyJWT 2.12.1 accepts the forged token
- Breach: The attacker now has admin access without knowing the actual admin credentials
This type of vulnerability is particularly dangerous because:
- It requires no network interception or key compromise
- It affects all applications using the vulnerable library version
- It completely bypasses the authentication mechanism
- Detection is difficult without proper logging of token verification failures
The Fix
The fix for CVE-2026-48526 was implemented in PyJWT 2.13.0, released on May 21, 2026. The upgrade path is straightforward but critical:
Before (PyJWT 2.12.1)
[[package]]
name = "pyjwt"
version = "2.12.1"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c2/27/a3b6e5bf6ff856d2509292e95c8f57f0df7017cf5394921fc4e4ef40308a/pyjwt-2.12.1.tar.gz", hash = "sha256:c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b", size = 102564 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e5/7a/8dd906bd22e79e47397a61742927f6747fe93242ef86645ee9092e610244/pyjwt-2.12.1-py3-none-any.whl", hash = "sha256:28ca37c070cad8ba8cd9790cd940535d40274d22f80ab87f3ac6a713e6e8454c", size = 29726 }
]
After (PyJWT 2.13.0)
[[package]]
name = "pyjwt"
version = "2.13.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/3b/81/58d0ac84e1ef3a3843791d6954d94c0b33d526c75eeb1efbce9d0a4c4077/pyjwt-2.13.0.tar.gz", hash = "sha256:41571c89ca91598c79e8ef18a2d07367d4810fbbd6f637794879baf1b7703423", size = 107515 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a3/5e/ecf12fdb62546d64385c158514e9b2b671f7832108ef2ecd2020ce0af2d1/pyjwt-2.13.0-py3-none-any.whl", hash = "sha256:66adcc2aff09b3f1bbd95fc1e1577df8ac8723c978552fd43304c8a290ac5728", size = 31274 }
]
What Changed
The upgrade from PyJWT 2.12.1 to 2.13.0 includes:
- Enhanced Signature Verification: Stricter validation of the cryptographic signature to prevent bypass techniques
- Improved Algorithm Handling: Better enforcement of the expected algorithm, preventing algorithm confusion attacks
- Token Structure Validation: More rigorous checks on token format and structure before processing claims
- Claim Validation: Strengthened validation of standard JWT claims like
exp(expiration) andiat(issued at)
The security improvements in PyJWT 2.13.0 ensure that:
- Forged tokens are properly rejected
- Signature verification cannot be bypassed
- Algorithm confusion attacks are prevented
- The validation logic is cryptographically sound
Implementation Steps
To fix this vulnerability in your project:
Step 1: Update your dependency file
# If using pip with requirements.txt
pip install --upgrade pyjwt>=2.13.0
# If using Poetry
poetry update pyjwt
# If using uv (as in the PR)
uv lock --upgrade pyjwt
Step 2: Verify the upgrade
pip show pyjwt
# Should show version 2.13.0 or later
Step 3: Test your application
# Run your test suite to ensure JWT validation still works
pytest tests/auth/
Step 4: Redeploy
# Rebuild and redeploy your application with the updated dependency
docker build -t myapp:latest .
Prevention & Best Practices
To protect against JWT-related vulnerabilities in the future:
1. Always Use Current Versions
- Regularly update PyJWT and other security-critical libraries
- Set up automated dependency scanning with tools like Trivy, Snyk, or Dependabot
- Subscribe to security advisories for libraries you depend on
2. Validate Algorithm Explicitly
# Good: Explicitly specify expected algorithm
payload = jwt.decode(
token,
secret_key,
algorithms=["HS256"] # Whitelist only expected algorithms
)
# Bad: Accepting any algorithm
payload = jwt.decode(token, secret_key) # Don't do this
3. Verify All Claims
# Always validate critical claims
payload = jwt.decode(
token,
secret_key,
algorithms=["HS256"],
options={
"verify_signature": True,
"verify_exp": True,
"verify_iat": True
}
)
# Check business logic claims
if payload.get("role") not in ["admin", "user"]:
raise ValueError("Invalid role in token")
4. Implement Proper Key Management
- Store secret keys securely (use environment variables or key management services)
- Rotate keys regularly
- Use different keys for different environments (dev, staging, production)
- Never commit keys to version control
5. Use Security Scanning
- Integrate Trivy or similar tools into your CI/CD pipeline
- Scan for known vulnerabilities in dependencies
- Set up alerts for new CVEs affecting your dependencies
- Review security advisories regularly
6. Log and Monitor
import logging
logger = logging.getLogger(__name__)
def verify_token(token, secret_key):
try:
payload = jwt.decode(token, secret_key, algorithms=["HS256"])
logger.info(f"Token verified for user: {payload.get('user_id')}")
return payload
except jwt.InvalidTokenError as e:
logger.warning(f"Token verification failed: {e}")
return None
7. Reference Security Standards
- OWASP JWT Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html
- CWE-347: https://cwe.mitre.org/data/definitions/347.html (Improper Verification of Cryptographic Signature)
- RFC 7519: https://tools.ietf.org/html/rfc7519 (JWT standard specification)
Key Takeaways
- Token forgery is a complete authentication bypass: A single flaw in JWT validation can allow attackers to impersonate any user in your system
- PyJWT 2.12.1 had insufficient signature verification: The vulnerability allowed crafted tokens to bypass security checks that should have rejected them
- The upgrade to 2.13.0 is mandatory: If you're using PyJWT 2.12.1 or earlier, update immediately—this is not optional
- Explicit algorithm validation is essential: Always whitelist expected algorithms in your
jwt.decode()calls; never accept arbitrary algorithms - Automated scanning caught this: Security tools like Trivy detected the vulnerable version and flagged it before it could be exploited in production
How Orbis AppSec Detected This
Source: Dependency specification in src/fetch/uv.lock where PyJWT 2.12.1 was pinned as a project dependency
Sink: Any call to jwt.decode() in the application that uses the vulnerable PyJWT 2.12.1 library, which lacks proper signature verification
Missing control: The vulnerable version lacked enhanced signature verification and cryptographic validation checks that were added in 2.13.0
CWE: CWE-347 (Improper Verification of Cryptographic Signature)
Fix: Upgraded PyJWT from version 2.12.1 to 2.13.0, which includes improved token validation logic and cryptographic signature verification
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
Authentication bypass vulnerabilities like CVE-2026-48526 represent a critical threat to application security. A single flaw in JWT validation can compromise your entire authentication system, allowing attackers to impersonate users and access sensitive data or functionality.
The fix is straightforward: upgrade PyJWT to version 2.13.0 or later. However, this incident highlights a broader principle: security is not a one-time fix. By implementing the practices outlined in this post—regular dependency updates, explicit algorithm validation, comprehensive claim verification, and automated security scanning—you can significantly reduce your attack surface and protect your applications from similar vulnerabilities.
Remember that security libraries like PyJWT are constantly improved as researchers discover new attack vectors. Staying current with these libraries isn't just about fixing known vulnerabilities; it's about benefiting from ongoing security research and best practices. Make dependency updates a regular part of your security hygiene, and your applications will be more resilient against authentication attacks.