# PyJWT: Public keys in DER form are accepted as HMAC secrets, bypassing the CVE-2022-29217 guard

PyJWT's HMAC key guard only text-matches PEM/SSH markers, so a DER-encoded RSA public key passes as an HMAC secret, enabling forged HS256 tokens.

- Payout: Research
- Severity: high
- Program: PyJWT
- Found by: 0xSmiley
- Disclosed: 2026-09-29
- CVE: CVE-2026-102271
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-p4g4-x82p-q773
- Library page: https://bugbountylibrary.com/r/ghsa%3AGHSA-p4g4-x82p-q773
- Tags: area:oss domain:oss impact:ato impact:integrity kind:research platform:ghsa sev:high tech:python technique:incomplete-fix technique:source-review vuln:auth-bypass vuln:crypto

**Target:** jwt/algorithms.py HMACAlgorithm.prepare_key key validation
**Root cause:** The asymmetric-key guard uses text matchers (is_pem_format regex, is_ssh_key prefix check) instead of parsing the key, so binary DER ASN.1 (0x30 0x82) matches neither and is accepted as an HMAC secret.
**Steps:**
1. Install `pyjwt==2.13.0` and `cryptography`.
2. Generate an RSA key and export the public key as DER SubjectPublicKeyInfo and DER PKCS#1.
3. Forge an HS256 token by HMAC-SHA256 signing `{"alg":"HS256"}.{"user":"admin","role":"admin"}` with the DER bytes as the secret.
4. Call `jwt.decode(token, der, algorithms=["RS256","HS256"])`; the forged token verifies, while the same key in PEM raises InvalidKeyError.
**How found:** Noticed the guard relies on text markers only; reasoned that a DER-encoded key is binary ASN.1 starting 0x30 0x82 with no `-----BEGIN` or `ssh-` prefix, then confirmed via grep that no DER handling exists and tested all three DER encodings.
**Exploitation:** Attacker takes the application's public key (public by definition), re-encodes it to DER, and signs arbitrary claims with it as an HMAC secret. The app, configured to accept both HS* and RS* with that key passed as DER bytes, verifies the forged token and grants the attacker's chosen identity.
**Insight:** When a security guard is a string/regex matcher rather than a real parser, enumerate every alternate encoding of the same object (DER vs PEM, PKCS#1 vs SPKI, X.509 cert) — the check only covers the textual form it was written against.

