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.
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:
| |
Three parts separated by dots:
- Header:
{"alg":"HS256","typ":"JWT"}- declares the algorithm - Payload:
{"userId":123,"role":"user"}- your actual data - 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:
- Attacker intercepts a valid JWT
- Decodes the header and changes
"alg":"HS256"to"alg":"none" - Modifies the payload (changes
"role":"user"to"role":"admin") - Removes the signature entirely
- Server reads the algorithm from the header, sees
none, skips verification - Attacker is now admin
The forged token:
| |
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:
| |
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.
Related JWT Attacks
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.
| |
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:
| |
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.
| |
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.
| |
Run Automated Scans
Fixing JWT Vulnerabilities
The fix is straightforward: never trust the algorithm specified in the token.
Secure Implementation
| |
For asymmetric algorithms:
| |
Key Principles
- Whitelist algorithms explicitly - Never accept whatever the token claims
- Disable ’none’ - Most libraries have options to reject ’none’ algorithm
- Use established libraries - Don’t roll your own JWT verification
- Store secrets properly - Use environment variables, not code
- Don’t store JWTs in localStorage - That’s an XSS attack waiting to happen
Framework-Specific Fixes
Node.js (jsonwebtoken):
| |
Python (PyJWT):
| |
Java (jjwt):
| |
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?
Is my JWT library vulnerable?
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?
How do AI coding tools keep generating vulnerable JWT code?
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