· via dev.to (home feed)
Unvalidated JWT kid parameter enables SQL injection and path traversal attacks
A dev.to post documents how the JWT kid header, processed before signature verification, can carry SQL injection and path traversal payloads that hand attackers control of the verification key.

A post on dev.to details a class of JWT flaws that requires no cryptographic break: when the token's kid header parameter reaches database queries or filesystem reads without sanitisation, attackers can steer key selection toward values they control and forge tokens that pass verification.
A lookup hint the spec never guarded
According to the post, RFC 7517 Section 4.5 defines kid as a case-sensitive string whose structure is left entirely to the developer — no required format, no character allowlist, no enforcement mechanism. The timing is what matters. JWT headers are Base64url-decoded and interpreted before signature verification runs, so a server acts on the kid value before it has any reason to trust the token. If that value reaches a key lookup as raw input, the attacker substitutes the key instead of breaking it, and most HMAC libraries will verify with whatever string the lookup returns.
SQL injection in the key lookup
The vulnerable pattern is a query assembled by concatenation: the server selects a verification key from a table where kid equals the raw header string. An attacker injects a UNION payload that makes the database return a literal string of their choosing, which the server then uses as the HMAC key. Because the attacker picked that key, they can re-sign a forged payload with it and the check succeeds — the signature is mathematically correct, and both the key and the signature originate from the attacker. A simpler OR-based payload returns the first row of the keys table, which the post notes can be made predictable with ORDER BY. The author cites Invicti's assessment of this flaw class: CVSS 3.1 7.5 High, combining CWE-89 and CWE-287, exploitable over the network with no prior authentication.
Path traversal to a zero-byte key
The filesystem variant concatenates kid into a key file path with no basename stripping or allowlist. An attacker points it at /dev/null, the Linux pseudo-device that always reads as empty, so the server derives a zero-byte HMAC key. The attacker then signs their token with a key consisting of a single null byte, Base64url-encoded as AA==, and both sides arrive at the same key by different routes: nothing at all. The post points to a PortSwigger lab documenting the full chain — a traversal payload in kid, the sub claim changed to administrator, a null-byte signing key — resulting in admin access. This variant is also rated 7.5 High under CWE-287.
kid as the entry point to the jku chain
The parameter also composes with jku, a header that tells the server to download its key set from a URL. Without a domain allowlist on that URL, an attacker hosts their own JWKS endpoint, sets jku to it, sets kid to a key in that set, and signs with the corresponding private key. dev.to draws a parallel to CVE-2018-0114, which affected node-jose before 0.11.0 with a CVSS of 8.1: the library trusted a JWK embedded in the token header itself. The structural failure is identical — key selection delegated to attacker-controlled input.
Why WAFs miss it
Per the post, most web application firewalls do not inspect JWT headers by default, and kid arrives Base64url-encoded, so detection rules must decode the header before patterns such as ../, %2F or UNION can match. The author mentions a reconnaissance tool, MAGO Intel, that extracts and checks kid values, and notes that HS256 setups are more exposed than RS256 — while stressing that scanning supplements rather than replaces validation in code.
Three controls that close the gap
The post prescribes three fixes:
- Parameterised queries, so kid is bound as data rather than spliced into SQL text.
- Path normalisation plus an allowlist: strip to a basename, reject values containing slashes or dot-dot sequences, and validate against a known set of key IDs — UUID format or a conservative pattern such as ^[a-zA-Z0-9_-]{1,64}$. Reject on mismatch and never fall back to a default key, which is its own attack surface.
- Algorithm pinning: check the alg header before the key lookup. RS256 and ES256 are immune to the empty-HMAC variant because an attacker cannot forge an asymmetric signature with a public key. Managed key stores such as AWS KMS or HashiCorp Vault remove raw SQL and file paths entirely, mapping kid to a key alias.
Why it matters
These attacks sidestep cryptography rather than defeating it: the signature check passes, the database or filesystem behaves normally, and nothing looks anomalous to the application. The kid field is a standard part of countless production JWT flows, and the spec explicitly leaves its handling to developers — while, according to the post, most libraries validate nothing and most WAFs inspect nothing by default. The mitigations are ordinary engineering hygiene — parameterised queries, allowlists, pinned algorithms — which makes this a cheap fix with a high payoff, and a pattern worth auditing in any JWT verification path that resolves keys dynamically.
- #jwt
- #security
- #sql-injection
- #path-traversal
- #authentication