JWT JKU Header Injection
| Severity | High |
|---|---|
| Classifications | CWE-346: Origin Validation Error |
| Attack Pattern | CAPEC-194: Fake the Source of Data |
| OWASP Category | OWASP API2:2023 Broken Authentication |
The jku JWT header field (RFC 7515 §4.1.2) tells the verifier where to fetch the JWK Set (JWKS) containing the key needed to verify the token’s signature. It exists so a verifier can dynamically resolve a signer’s public key by URL instead of storing it ahead of time. Servers that dereference jku without checking it against a known, pinned set of trusted hosts will fetch and trust a key from any URL an attacker supplies — including one the attacker controls.
An attacker who controls this trust gap generates their own key pair, hosts a JWKS document containing the public half at a URL they control, points the token’s jku header at that URL, and signs the token with the matching private key. Since the server fetches and uses the attacker-supplied key to verify the attacker-supplied signature, the check always passes — for any claims the attacker chooses.
This is the same class of trust failure as JWK header injection (CVE-2018-0114) — the difference is where the malicious key lives: embedded directly in the token (jwk) versus fetched from an attacker-hosted URL (jku).
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, hosts a JWKS with the public key at a URL they control, sets jku (and kid to match), and re-signs with the private half:
{ "alg": "RS256", "typ": "JWT", "kid": "jku-injection", "jku": "https://attacker.example/.well-known/jwks.json"}A vulnerable server fetches https://attacker.example/.well-known/jwks.json, reads the public key out of it, verifies the signature against it (which succeeds — the attacker signed with the matching private key), and treats the token as authentic. Restricting jku to https or requiring it to match the token’s issuer host does not help if the attacker can host content on a permitted domain (e.g. via an open redirect, a shared CDN, or a subdomain takeover).
How to test?
Section titled “How to test?”If you want to test if your server is vulnerable to the “JWT JKU Header Injection” vulnerability, use the crack command with --jku-server-addr set to a bind address reachable by the target — jwtop spins up a throwaway local JWKS server, points the forged token’s jku header at it, and probes the target:
jwtop crack [token] --url [url] --jku-server-addr 0.0.0.0:8089The bind address must be reachable by the server under test — use a public interface/port (or a tunnel such as ngrok) when testing a remote target rather than a loopback address. The check is skipped when --jku-server-addr is omitted, and only runs against RSA/ECDSA (RS*/ES*/PS*) tokens — a server that trusts a fetched jku key has no reason to do so for HMAC-signed tokens.
To generate a standalone token with a jku header pointed at a JWKS URL you already control, use the exploit jkuinjection command:
jwtop exploit jkuinjection [token] --url https://attacker.example/.well-known/jwks.jsonjwtop exploit jkuinjection [token] --alg ES256 --url https://attacker.example/.well-known/jwks.jsonjwtop exploit jkuinjection [token] --key /path/to/private.pem --url https://attacker.example/.well-known/jwks.jsonexploit jkuinjection only sets the header and signs the token — it does not host the JWKS itself, since --url may point at infrastructure you already control. The crack command’s --jku-server-addr flag is the one that also serves the JWKS.
What is the impact?
Section titled “What is the impact?”A successful JKU 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. It can also be used as a Server-Side Request Forgery (SSRF) primitive, since the server makes an outbound HTTP request to an attacker-chosen URL.
How to remediate?
Section titled “How to remediate?”- Never resolve the verification key from the token itself. Ignore the
jku(andjwk,x5u) header fields when verifying — look up the key server-side bykidagainst a pinned keyset or your own JWKS endpoint. - If
jkusupport is unavoidable, restrict it to a strict allowlist of trusted, fully-qualified JWKS URLs — never a pattern match on domain suffix alone — and never follow redirects when fetching it. - Pin the expected algorithm and key(s) per issuer so a token cannot dictate which key or algorithm family verifies it.
- Treat the
jkufetch as an outbound request to an untrusted destination: block internal/private IP ranges to prevent it being used as an SSRF vector against internal services.