Because JSON Web Tokens are self-contained and publicly readable, any flaw in how a server verifies signatures or processes headers can lead to critical authentication bypasses and remote code execution.
In this guide, we analyze the 5 most famous real-world JWT attack vectors, demonstrate how attackers exploit them, and show you the exact defensive code to prevent them.
1. Attack 1: The "alg: none" Signature Bypass
The JWT specification (RFC 7519 Section 6) supports an "Unsecured JWT" mode where alg is set to "none". In this mode, no signature is generated or checked.
⚠️ Exploit Walkthrough: The "alg: none" Signature Bypass Attack
Security Vulnerability💥 How the Exploit Works
An attacker takes a legitimate token, changes the payload claim "role": "user" to "role": "admin", and modifies the header to:
{"alg": "none", "typ": "JWT"}
The attacker strips the signature from the end:
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ.
Vulnerable JWT libraries historically accepted this token as completely valid, granting the attacker instant administrative privileges!
Defensive Remediation:
Modern libraries reject none by default, but you should always enforce explicit algorithm whitelisting:
// REJECTS none automatically by specifying allowed algorithms
jwt.verify(token, secretOrKey, {
algorithms: ["RS256", "HS256"] // "none" is strictly disallowed
});
2. Attack 2: The HMAC vs. RSA Algorithm Confusion Attack
This vulnerability occurs when a server is designed to verify tokens using an asymmetric algorithm (like RS256), but fails to verify that the token's header actually specifies RS256.
💥 How the Exploit Works
- The server signs tokens using an RSA private key and verifies them using the corresponding RSA public key.
- RSA public keys are public (often downloadable via JWKS). The attacker downloads the server's public key in PEM format.
- The attacker modifies their token payload (e.g., granting themselves admin rights).
- The attacker changes the header algorithm from
RS256toHS256. - The attacker signs the modified token using HS256 with the server's public key PEM text as the HMAC secret!
- When the server receives the token, it executes:
jwt.verify(token, serverPublicKey) - Because the token specifies
HS256, the library treatsserverPublicKeyas an HMAC shared secret. Since the attacker signed the token with that exact same public key text, the signature verification succeeds!
Defensive Remediation:
Strictly bind your verification logic to the expected algorithm:
jwt.verify(token, serverPublicKey, {
algorithms: ["RS256"] // Disallows HS256 when verifying with an RSA key
});
3. Attack 3: Offline Secret Brute-Forcing
Because a JWT's header and payload are visible, an attacker with a valid HS256 token can perform offline dictionary attacks without ever making a network request to your server.
Using tools like Hashcat (mode 16500) or John the Ripper, modern GPUs can test billions of candidate passwords per second against an HMAC-SHA256 signature. If your secret is secret, password123, or company-jwt-token, it will be cracked in seconds. Once cracked, the attacker can forge unlimited valid tokens.
Defensive Remediation:
Always use high-entropy cryptographic secrets:
openssl rand -hex 32
4. Attack 4: Key ID (`kid`) Header Injection
The JOSE header parameter kid (Key ID) tells the server which key to use when looking up secrets in a key store or database:
{"alg": "HS256", "typ": "JWT", "kid": "key-2026-v1"}
If the backend naively uses the kid in a filesystem lookup or database query without sanitization, severe vulnerabilities arise:
- Directory Traversal: An attacker sets
"kid": "../../dev/null". If the server reads the secret from/keys/${kid}, it reads/dev/null(an empty string). The attacker signs the token using an empty string""as the secret, bypassing authentication! - SQL Injection: If
kidis used inSELECT secret FROM keys WHERE id = '${header.kid}', an attacker can inject' UNION SELECT 'my_secret' --and sign the token withmy_secret.
Defensive Remediation:
Never interpolate kid into raw filesystem paths or database queries. Validate kid against an explicit in-memory dictionary or sanitize it strictly:
const VALID_KEYS = {
"key-2026-v1": process.env.KEY_V1,
"key-2026-v2": process.env.KEY_V2
};
function getKey(header, callback) {
const secret = VALID_KEYS[header.kid];
if (!secret) return callback(new Error("Unknown Key ID"));
callback(null, secret);
}
5. Attack 5: JKU / X5U Header URL Spoofing (SSRF)
The jku (JWK Set URL) header specifies a remote URL from which the recipient should fetch the public key. If the server fetches this URL automatically without validation, an attacker can:
- Point
jkuto an attacker-controlled server (https://evil.com/jwks.json) hosting a forged public key. - Trigger Server-Side Request Forgery (SSRF) against internal cloud metadata endpoints (e.g.
http://169.254.169.254/).
Defensive Remediation:
Never fetch arbitrary URLs specified in JWT headers. Whitelist allowed domains or hardcode your trusted authorization server JWKS endpoint in your API configuration.