# Empty HMAC key skips signature verification

Falsy or zero-length keys and empty signatures reaching verification without the empty-key guard

_Area: Web authentication and account takeover. Seen in 3 disclosed reports; latest 2026-10-22. Updated 2026-10-24._

## How it works
Verification code assumes that if a key object exists, it is a usable secret. When the key is empty (`""`, `null`, `b""`, or an `oct` JWK with `k=""`), that assumption breaks: the empty-key rejection is skipped by a truthiness guard, or never reached because a sibling code path bypasses it. The signature check then runs against nothing, or is gated on a truthy signature that an attacker simply omits, so a forged or unsigned token is accepted.

## Where it shows up
- Key-loading branches guarded by `if (key && ...)` or similar truthiness tests, so `''`/`null` never reach the empty-secret check [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-8wpc-h4q6-8fxv.md)
- Signature verification gated on a truthy signature value, letting an empty signature fall through [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-8wpc-h4q6-8fxv.md)
- Multiple key-ingestion paths (raw bytes vs `from_jwk`/`PyJWK`/`PyJWKSet`/`PyJWKClient`) where only one applies the empty-key guard [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-pxh4-856f-4h89.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-9j54-fg26-wv3r.md)
- `_verify_signature` using `key.key` directly instead of routing through `prepare_key()` [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-9j54-fg26-wv3r.md)
- `from_jwk` base64url-decoding a JWK `k` field with no zero-length check, so `k=""` yields `b""` [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-pxh4-856f-4h89.md)

## How to review
The invariant: every path that produces key material for verification must reject zero-length or falsy secrets before any signature comparison, and no signature check may be skipped when the signature is absent. Trace each key source (string, Buffer, async, JWK, JWKS client) to the comparison and confirm the same guard applies to all. Confirm an empty signature is a failure, not a pass. For JWK routes, check that decoded `k` bytes are validated the same way as raw bytes.

## Variants seen
- Falsy sync key plus explicit `algorithms` allowlist skips verification entirely [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-8wpc-h4q6-8fxv.md)
- `oct` JWK with empty `k` accepted via `from_jwk`, bypassing `prepare_key` [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-pxh4-856f-4h89.md)
- `PyJWK.key` used directly, skipping the empty-key check [E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-9j54-fg26-wv3r.md)

## Why fixes fail
A guard added to `prepare_key` was never propagated to `from_jwk`/`PyJWK`; the test only covered empty JSON `{}`, not an `oct` key with empty `k` [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-pxh4-856f-4h89.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-9j54-fg26-wv3r.md). Hardening covered Buffer and async paths but missed sync `''`/`null` [E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-8wpc-h4q6-8fxv.md).

## Impact and severity
Forged tokens with arbitrary claims and authentication bypass [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-pxh4-856f-4h89.md)[E2](https://bugbountylibrary.com/r/ghsa%3AGHSA-8wpc-h4q6-8fxv.md)[E3](https://bugbountylibrary.com/r/ghsa%3AGHSA-9j54-fg26-wv3r.md). Severity is high: the flaw is silent, needs no secret, and the length warning is non-fatal by default [E1](https://bugbountylibrary.com/r/ghsa%3AGHSA-pxh4-856f-4h89.md).

## Evidence
- E1: https://bugbountylibrary.com/r/ghsa%3AGHSA-pxh4-856f-4h89.md
- E2: https://bugbountylibrary.com/r/ghsa%3AGHSA-8wpc-h4q6-8fxv.md
- E3: https://bugbountylibrary.com/r/ghsa%3AGHSA-9j54-fg26-wv3r.md
