Skip to content

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

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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