Security Advisory
Reported by: Conner Webber (conner.webber000@gmail.com)
Note: Private vulnerability reporting (PVRA) is disabled on this repo and no SECURITY.md exists, so filing here. These findings extend beyond CVE-2024-33663 (OpenSSH ECDSA algorithm confusion).
Finding 1: Algorithm Confusion via DER-Encoded Keys (CRITICAL, CWE-327)
Files: jose/backends/cryptography_backend.py:543, jose/backends/native.py:39
CVE-2024-33663 fixed algorithm confusion for OpenSSH ECDSA keys, but DER-encoded RSA public keys remain undetected. The HMAC key constructor checks if is_pem_format(key) or is_ssh_key(key) but does NOT detect DER-encoded keys. An attacker can:
- Obtain the server's RSA public key in DER format (or convert PEM→DER)
- Sign a forged JWT with HS256 using the raw DER bytes as the HMAC secret
- If the server calls
jwt.decode(token, public_key_der) without specifying algorithms, the token validates
This bypasses the existing OpenSSH key format check added for CVE-2024-33663.
Finding 2: Default algorithms=None Still Skips Whitelist (HIGH, CWE-757)
Files: jose/jwt.py:66, jose/jws.py:258
Related to CVE-2024-33663 but still unfixed: jwt.decode() defaults algorithms=None. When None, the algorithm whitelist check at jws.py:258 is skipped entirely, allowing attacker-controlled algorithm selection from the JWT header. PyJWT made algorithms required after CVE-2022-29217 — python-jose has not.
Finding 3: Empty HMAC Key Accepted (HIGH, CWE-326)
Files: jose/backends/cryptography_backend.py:527, jose/backends/native.py:23
Both HMAC backends accept empty string ("") or empty bytes (b"") as valid signing keys. An empty HMAC key produces deterministic signatures that anyone can forge. No validation rejects zero-length keys.
import jose.jwt as jwt
# This succeeds — should raise an error
token = jwt.encode({"admin": True}, "", algorithm="HS256")
jwt.decode(token, "", algorithms=["HS256"]) # Valid
Finding 4: Non-Constant-Time JWE Auth Tag Comparison (MEDIUM, CWE-208)
File: jose/jwe.py:247
JWE authentication tag verification uses Python != operator instead of hmac.compare_digest(), enabling timing side-channel attacks to forge valid auth tags byte-by-byte.
Finding 5: Non-Constant-Time at_hash Comparison (MEDIUM, CWE-208)
File: jose/jwt.py:471
OpenID Connect at_hash claim verification uses Python != instead of constant-time comparison, enabling timing attacks.
Impact
- Finding 1+2: Full authentication bypass — attacker with the RSA public key (often publicly available) can forge arbitrary JWT tokens
- Finding 3: Token forgery if any code path uses an empty key
- Finding 4+5: Timing oracle for JWE tag and OIDC at_hash forgery
Recommended Fixes
- Add DER format detection to HMAC key validation (check for ASN.1 SEQUENCE header
0x30)
- Make
algorithms a required parameter in jwt.decode() (breaking change, but PyJWT did this)
- Reject empty HMAC keys with
ValueError
- Replace
!= with hmac.compare_digest() in jwe.py:247 and jwt.py:471
References
Security Advisory
Reported by: Conner Webber (conner.webber000@gmail.com)
Note: Private vulnerability reporting (PVRA) is disabled on this repo and no SECURITY.md exists, so filing here. These findings extend beyond CVE-2024-33663 (OpenSSH ECDSA algorithm confusion).
Finding 1: Algorithm Confusion via DER-Encoded Keys (CRITICAL, CWE-327)
Files:
jose/backends/cryptography_backend.py:543,jose/backends/native.py:39CVE-2024-33663 fixed algorithm confusion for OpenSSH ECDSA keys, but DER-encoded RSA public keys remain undetected. The HMAC key constructor checks
if is_pem_format(key) or is_ssh_key(key)but does NOT detect DER-encoded keys. An attacker can:jwt.decode(token, public_key_der)without specifyingalgorithms, the token validatesThis bypasses the existing OpenSSH key format check added for CVE-2024-33663.
Finding 2: Default
algorithms=NoneStill Skips Whitelist (HIGH, CWE-757)Files:
jose/jwt.py:66,jose/jws.py:258Related to CVE-2024-33663 but still unfixed:
jwt.decode()defaultsalgorithms=None. When None, the algorithm whitelist check atjws.py:258is skipped entirely, allowing attacker-controlled algorithm selection from the JWT header. PyJWT madealgorithmsrequired after CVE-2022-29217 — python-jose has not.Finding 3: Empty HMAC Key Accepted (HIGH, CWE-326)
Files:
jose/backends/cryptography_backend.py:527,jose/backends/native.py:23Both HMAC backends accept empty string (
"") or empty bytes (b"") as valid signing keys. An empty HMAC key produces deterministic signatures that anyone can forge. No validation rejects zero-length keys.Finding 4: Non-Constant-Time JWE Auth Tag Comparison (MEDIUM, CWE-208)
File:
jose/jwe.py:247JWE authentication tag verification uses Python
!=operator instead ofhmac.compare_digest(), enabling timing side-channel attacks to forge valid auth tags byte-by-byte.Finding 5: Non-Constant-Time
at_hashComparison (MEDIUM, CWE-208)File:
jose/jwt.py:471OpenID Connect
at_hashclaim verification uses Python!=instead of constant-time comparison, enabling timing attacks.Impact
Recommended Fixes
0x30)algorithmsa required parameter injwt.decode()(breaking change, but PyJWT did this)ValueError!=withhmac.compare_digest()injwe.py:247andjwt.py:471References
algorithmsparam)