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
1. Legitimate Token {"alg": "HS256"} {"user": "bob", "admin": false} Valid HMAC Signature 2. Attacker Tampering {"alg": "none"} 🚨 {"user": "bob", "admin": true} (Empty Signature: "") 3. Vulnerable Server Reads "alg": "none" Skips Signature Check! FORGED ADMIN ACCESS
Figure 1: In the "alg: none" vulnerability, flawed libraries trust the unverified header algorithm, enabling attackers to forge admin privileges simply by omitting the signature.

💥 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:

Defensive Code: Explicit Algorithm Whitelist
// 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

  1. The server signs tokens using an RSA private key and verifies them using the corresponding RSA public key.
  2. RSA public keys are public (often downloadable via JWKS). The attacker downloads the server's public key in PEM format.
  3. The attacker modifies their token payload (e.g., granting themselves admin rights).
  4. The attacker changes the header algorithm from RS256 to HS256.
  5. The attacker signs the modified token using HS256 with the server's public key PEM text as the HMAC secret!
  6. When the server receives the token, it executes:
    jwt.verify(token, serverPublicKey)
  7. Because the token specifies HS256, the library treats serverPublicKey as 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:

Defensive Code: Prevent Key Confusion
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:

Generate a 256-bit Cryptographic Secret
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:

Defensive Remediation:

Never interpolate kid into raw filesystem paths or database queries. Validate kid against an explicit in-memory dictionary or sanitize it strictly:

Defensive Code: Whitelist Key IDs
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:

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.