Skip to content

Security: DER key algorithm confusion, empty HMAC key, and timing side-channels #398

@spartan8806

Description

@spartan8806

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:

  1. Obtain the server's RSA public key in DER format (or convert PEM→DER)
  2. Sign a forged JWT with HS256 using the raw DER bytes as the HMAC secret
  3. 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

  1. Add DER format detection to HMAC key validation (check for ASN.1 SEQUENCE header 0x30)
  2. Make algorithms a required parameter in jwt.decode() (breaking change, but PyJWT did this)
  3. Reject empty HMAC keys with ValueError
  4. Replace != with hmac.compare_digest() in jwe.py:247 and jwt.py:471

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions