JWT JWK Header Injection
| Severity | High |
|---|---|
| Classifications | CWE-345: Insufficient Verification of Data Authenticity |
| Attack Pattern | CAPEC-194: Fake the Source of Data |
| OWASP Category | OWASP API2:2023 Broken Authentication |
The jwk JWT header field (RFC 7515 §4.1.3) lets a token embed the public key needed to verify its own signature. It exists for cases where the verifier doesn’t yet have the signer’s key material. Some JWT libraries — notably early versions of express-jwt/node-jsonwebtoken (CVE-2018-0114) — implicitly trusted whatever key was embedded there instead of checking it against a known, pinned keyset or the server’s own JWKS endpoint.
An attacker who controls this trust gap simply generates their own key pair, embeds the public half in the jwk header, and signs the token with the matching private key. Since the server extracts and uses the attacker-supplied key to verify the attacker-supplied signature, the check always passes — for any claims the attacker chooses.
Example
Section titled “Example”Here is a valid JWT signed with RS256 by the server’s real key:
eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIn0.<signature>The attacker generates a fresh RSA key pair, embeds the public key in the jwk header, sets kid to match, and re-signs with the private half:
{ "alg": "RS256", "typ": "JWT", "kid": "jwk-injection", "jwk": { "kty": "RSA", "use": "sig", "kid": "jwk-injection", "alg": "RS256", "n": "xXMZbZPrGVs550WbJ0FJRMLijFY7wDDME1dV88KGQPzJ...", "e": "AQAB" }}A vulnerable server reads the public key straight out of jwk, verifies the signature against it (which succeeds — the attacker signed with the matching private key), and treats the token as authentic.
How to test?
Section titled “How to test?”If you want to test if your server is vulnerable to the “JWT JWK Header Injection” vulnerability, you can use the crack command:
jwtop crack [token] --url [url]The check only runs against RSA/ECDSA (RS*/ES*/PS*) tokens — a server that trusts an embedded jwk has no reason to do so for HMAC-signed tokens.
To generate a token with a self-signed jwk injected, use the exploit jwkinjection command:
jwtop exploit jwkinjection [token]jwtop exploit jwkinjection [token] --alg ES256jwtop exploit jwkinjection [token] --key /path/to/private.pemWhat is the impact?
Section titled “What is the impact?”A successful JWK injection attack allows an attacker to forge a JWT that the server accepts as legitimate, with no knowledge of the server’s actual signing key. This can lead to full authentication bypass, privilege escalation, or impersonation of any user in the system.
How to remediate?
Section titled “How to remediate?”- Never resolve the verification key from the token itself. Ignore the
jwk(andjku,x5u) header fields when verifying — look up the key server-side bykidagainst a pinned keyset or your own JWKS endpoint. - If
jwk/jkusupport is unavoidable, restrict it to an explicit allowlist of trusted keys or issuers, and never accept a key the token itself supplies as proof of its own authenticity. - Pin the expected algorithm and key(s) per issuer so a token cannot dictate which key or algorithm family verifies it.