The JWT Vulnerability That Still Works in 2026 (And How to Stop It)

The JWT Vulnerability That Still Works in 2026 (And How to Stop It)

The Attack That Won’t Die

I found three apps last month with this exact vulnerability. All built with AI coding tools. All in production with real users.

JSON Web Token (JWT) : A compact, URL-safe token format for transmitting claims between parties. JWTs contain three base64-encoded parts: header (algorithm and type), payload (claims like user ID and expiration), and signature (cryptographic verification).

JWTs are everywhere in 2026. Every auth tutorial uses them. Every AI coding assistant generates them. And that’s exactly the problem.

That’s how many AI-generated applications we’ve scanned that have JWT vulnerabilities. The ’none’ algorithm attack is the most common.

How JWT Signatures Work

A typical JWT looks like this:

1
2
3
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VySWQiOjEyMywicm9sZSI6InVzZXIifQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Three parts separated by dots:

  1. Header: {"alg":"HS256","typ":"JWT"} - declares the algorithm
  2. Payload: {"userId":123,"role":"user"} - your actual data
  3. Signature: HMAC-SHA256 of header + payload using a secret key

The server verifies the signature before trusting the payload. If the signature doesn’t match, the token is rejected. Simple and secure.

Until someone changes the algorithm.

The ’none’ Algorithm Attack

The JWT spec includes a special algorithm value: none. It means “no signature required.” This exists for debugging and specific trust scenarios where tokens are transmitted over already-secure channels.

Here’s how the attack works:

  1. Attacker intercepts a valid JWT
  2. Decodes the header and changes "alg":"HS256" to "alg":"none"
  3. Modifies the payload (changes "role":"user" to "role":"admin")
  4. Removes the signature entirely
  5. Server reads the algorithm from the header, sees none, skips verification
  6. Attacker is now admin

The forged token:

1
2
eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.
eyJ1c2VySWQiOjEyMywicm9sZSI6ImFkbWluIn0.

No signature. Full access.

Why This Still Happens

Most JWT libraries support ’none’ by default. The library reads the algorithm from the token header and uses it for verification. If the token says “don’t verify me,” the library complies.

Here’s vulnerable Node.js code I see constantly:

1
2
// VULNERABLE - trusts the algorithm in the token
const decoded = jwt.verify(token, secret);

The jwt.verify function reads the alg header from the token itself. If an attacker sends a token with alg: "none", some libraries skip verification entirely.

AI coding assistants love generating this pattern because it’s simple and it works in happy-path testing.

The ’none’ algorithm is just one attack in a family of algorithm confusion vulnerabilities.

Algorithm Confusion (RS256 to HS256)

RS256 uses asymmetric cryptography: sign with private key, verify with public key. HS256 uses symmetric: same secret for both.

Attack: Server is configured for RS256. Attacker changes the token header to HS256, signs with the public key (which is often publicly available), and the server accepts it because it treats the public key as the HMAC secret.

1
2
3
4
// Server expects RS256 (asymmetric)
// Attacker sends token with alg: HS256
// Server verifies with public key as HMAC secret
// Signature matches because attacker signed with same public key

JKU/JWK Injection

Some JWT implementations fetch the verification key from a URL specified in the token header (jku claim). Attacker points this to their own server hosting a malicious key.

kid Path Traversal

The kid (key ID) header tells the server which key to use. If the server uses this value in a file path without validation:

1
2
// VULNERABLE
const key = fs.readFileSync(`/keys/${token.header.kid}`);

Attacker sets kid to ../../../dev/null and the signature check passes against an empty file.

Detecting JWT Vulnerabilities

Testing Your JWT Implementation

Steps to check if your application is vulnerable to JWT algorithm attacks

Test for 'none' Algorithm Acceptance

Take a valid JWT from your app. Decode it (base64), change the alg to "none", modify the payload, and remove the signature. Send it to your API. If it’s accepted, you’re vulnerable.

1
2
3
4
5
6
7
# Original token
echo "eyJhbGciOiJIUzI1NiJ9.eyJ1c2VySWQiOjF9.signature" | cut -d. -f1 | base64 -d
# {"alg":"HS256"}

# Forge token with none
echo -n '{"alg":"none","typ":"JWT"}' | base64 | tr -d '='
# eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0

Test for Algorithm Confusion

If your app uses RS256, try sending a token signed with HS256 using the public key as the secret. Tools like jwt_tool automate this.

1
jwt_tool <token> -X a -pk public_key.pem

Run Automated Scans

Use Vibe-Eval’s JWT security check to automatically test for these vulnerabilities. It decodes tokens in-flight, validates algorithms, checks expiration claims, and flags insecure storage patterns like localStorage.

Fixing JWT Vulnerabilities

The fix is straightforward: never trust the algorithm specified in the token.

Secure Implementation

1
2
3
4
// SECURE - explicitly specify allowed algorithms
const decoded = jwt.verify(token, secret, {
  algorithms: ['HS256']  // Whitelist only what you use
});

For asymmetric algorithms:

1
2
3
4
// SECURE - RS256 with algorithm whitelist
const decoded = jwt.verify(token, publicKey, {
  algorithms: ['RS256']
});

Key Principles

  1. Whitelist algorithms explicitly - Never accept whatever the token claims
  2. Disable ’none’ - Most libraries have options to reject ’none’ algorithm
  3. Use established libraries - Don’t roll your own JWT verification
  4. Store secrets properly - Use environment variables, not code
  5. Don’t store JWTs in localStorage - That’s an XSS attack waiting to happen

Framework-Specific Fixes

Node.js (jsonwebtoken):

1
jwt.verify(token, secret, { algorithms: ['HS256'] });

Python (PyJWT):

1
jwt.decode(token, secret, algorithms=['HS256'])

Java (jjwt):

1
2
3
4
Jwts.parserBuilder()
    .setSigningKey(key)
    .build()
    .parseClaimsJws(token);  // Rejects none by default

How Vibe-Eval Catches This

Our scanner analyzes your application for JWT handling patterns:

  • Detects tokens in responses and decodes them
  • Tests algorithm manipulation attacks automatically
  • Flags localStorage JWT storage (XSS vector)
  • Checks for missing expiration claims
  • Identifies hardcoded secrets in JavaScript bundles

When we find JWT vulnerabilities, the report includes the specific attack vector and exact code location to fix.

FAQ

Why does the 'none' algorithm exist in the JWT spec?

The ’none’ algorithm was designed for scenarios where the token integrity is guaranteed by other means, like TLS client certificates or being transmitted inside an already-encrypted channel. It was never intended for general authentication. Most security experts consider its inclusion in the spec a mistake.

Is my JWT library vulnerable?

Most modern libraries have addressed this, but configuration matters. The jsonwebtoken library for Node.js, PyJWT for Python, and jjwt for Java all support algorithm whitelisting. The vulnerability occurs when developers don’t use these features. Always explicitly specify allowed algorithms.

Should I stop using JWTs entirely?

No. JWTs are fine when implemented correctly. The alternative (server-side sessions with database lookups) has its own trade-offs. The key is understanding JWT security properties and configuring your library properly. Stateless authentication is valuable for scaling.

How do AI coding tools keep generating vulnerable JWT code?

AI assistants optimize for working code, not secure code. The simple jwt.verify(token, secret) pattern works in tests and is commonly seen in tutorials. Without explicit security requirements in the prompt, AI generates the shortest path to functionality. Always review AI-generated auth code carefully.

Key Takeaways

Key Takeaways

  • The JWT ’none’ algorithm attack lets attackers forge tokens by removing signatures entirely
  • 35% of vibe-coded apps have JWT vulnerabilities because AI generates the simplest working code
  • Vulnerable code trusts the algorithm header in the token instead of enforcing server-side
  • Algorithm confusion attacks (RS256 to HS256) exploit asymmetric/symmetric key mismatches
  • Fix by whitelisting algorithms explicitly: jwt.verify(token, secret, { algorithms: ['HS256'] })
  • Never store JWTs in localStorage - use HttpOnly cookies to prevent XSS token theft
  • Tools like Vibe-Eval automatically detect these vulnerabilities before attackers do

AI Coding Security Insights.
Ship Vibe-Coded Apps Safely.

Effortlessly test and evaluate web application security using Vibe Eval agents.