JWT Vulnerabilities in AI-Generated APIs: Algorithm Confusion and None Attack - VibeDoctor 
← All Articles 🔐 Auth & Identity Critical

JWT Vulnerabilities in AI-Generated APIs: Algorithm Confusion and None Attack

AI tools generate JWT code with weak secrets, missing algorithm checks, and no expiry. Learn the attacks and how to implement JWT securely.

SEC-016 SEC-001 SEC-014

Quick Answer

AI-generated JWT implementations typically have three critical flaws: hardcoded secrets (often "secret" or "your-secret-key"), no algorithm restriction allowing the "none" algorithm attack, and missing token expiry. These three issues together let attackers forge valid tokens and access any account in your application.

Why AI Gets JWT Wrong

JSON Web Tokens (JWT) are the most common authentication mechanism in AI-generated APIs. Every AI coding tool, from Cursor to Bolt to Lovable, generates JWT-based authentication when asked for user login. The problem is that working JWT code and secure JWT code are very different things.

According to the Auth0 2024 State of Identity Security report, JWT misconfiguration is the most common authentication vulnerability in modern web applications. The OWASP Testing Guide specifically calls out algorithm confusion and weak secrets as top JWT risks. AI tools generate code that demonstrates these exact vulnerabilities.

A 2023 PortSwigger research study found that 25% of web applications using JWT had at least one exploitable implementation flaw. When AI generates the JWT code, that number climbs significantly because the models learn from tutorial-quality examples, not production-hardened implementations.

Vulnerability 1: Hardcoded JWT Secrets

// ❌ BAD - AI's favorite JWT patterns
const token = jwt.sign(payload, 'secret');
const token = jwt.sign(payload, 'your-secret-key');
const token = jwt.sign(payload, 'jwt-secret-123');
const token = jwt.sign(payload, process.env.JWT_SECRET || 'fallback-secret');
// ✅ GOOD - Strong secret, no fallback
// Generate with: node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"
const token = jwt.sign(payload, process.env.JWT_SECRET, {
  algorithm: 'HS256',
  expiresIn: '1h',
});
// JWT_SECRET must be at least 256 bits (64 hex chars) for HS256

Vulnerability 2: The "none" Algorithm Attack

The JWT specification includes an "alg": "none" option that creates unsigned tokens. If your verification code does not explicitly restrict algorithms, an attacker can modify a token, set the algorithm to "none", remove the signature, and your server will accept it as valid.

// ❌ BAD - No algorithm restriction
const decoded = jwt.verify(token, secret);
// Accepts ANY algorithm, including "none"

// ❌ BAD - Algorithm confusion: RS256 key used with HS256
const decoded = jwt.verify(token, publicKey);
// Attacker can use the public key as HMAC secret
// ✅ GOOD - Explicitly restrict algorithm
const decoded = jwt.verify(token, secret, {
  algorithms: ['HS256'],  // ONLY accept HS256
  complete: true,
});

Vulnerability 3: Missing or Excessive Token Expiry

AI-generated code either sets no expiry at all (tokens valid forever) or sets extremely long expiry periods. A stolen token with no expiry gives permanent access to the victim's account.

Token Type AI Default Secure Default Why
Access token No expiry or 30 days 15-60 minutes Limits damage window if stolen
Refresh token Not implemented 7-30 days, rotated on use Allows session extension safely
Password reset 24 hours or no expiry 15-30 minutes Minimizes attack window
Email verification No expiry 24 hours Prevents stale verification links

Complete Secure JWT Implementation

// ✅ GOOD - Production-ready JWT setup
import jwt from 'jsonwebtoken';

const JWT_SECRET = process.env.JWT_SECRET;
if (!JWT_SECRET || JWT_SECRET.length < 64) {
  throw new Error('JWT_SECRET must be set and at least 64 characters');
}

function generateTokens(userId) {
  const accessToken = jwt.sign(
    { sub: userId, type: 'access' },
    JWT_SECRET,
    { algorithm: 'HS256', expiresIn: '15m' }
  );
  const refreshToken = jwt.sign(
    { sub: userId, type: 'refresh' },
    JWT_SECRET,
    { algorithm: 'HS256', expiresIn: '7d' }
  );
  return { accessToken, refreshToken };
}

function verifyAccessToken(token) {
  return jwt.verify(token, JWT_SECRET, {
    algorithms: ['HS256'],
    complete: true,
  });
}

How to Audit Your JWT Implementation

  1. Search for hardcoded secrets: Grep for jwt.sign and check the secret parameter. If it is a string literal, replace with an env var.
  2. Check algorithm restrictions: Every jwt.verify call must include { algorithms: ['HS256'] } (or your chosen algorithm).
  3. Verify token expiry: Every jwt.sign call should include expiresIn.
  4. Look for refresh token rotation: If your app uses refresh tokens, ensure they are rotated (old token invalidated) on each use.
  5. Run automated scanning: Tools like VibeDoctor (vibedoctor.io) automatically detect hardcoded JWT secrets and insecure token patterns in your codebase. Free to sign up.

FAQ

Should I use JWT or session cookies for authentication?

For most web applications, session cookies with httpOnly and Secure flags are simpler and harder to get wrong. JWT is better for stateless APIs, microservices, and mobile app backends. If you are building a standard Next.js app, consider using NextAuth or Clerk instead of implementing JWT yourself.

What is algorithm confusion in JWT?

Algorithm confusion (or key confusion) happens when a server using RS256 (asymmetric) does not restrict the algorithm. An attacker can switch the algorithm to HS256 (symmetric) and sign the token with the public key, which the server then accepts. Always specify algorithms: ['RS256'] or algorithms: ['HS256'] in your verify call.

How long should my JWT secret be?

For HS256, the secret must be at least 256 bits (32 bytes, or 64 hex characters). Use crypto.randomBytes(64).toString('hex') to generate a secure secret. Never use dictionary words, short strings, or common phrases. The secret's entropy directly determines how resistant it is to brute-force attacks.

Can I invalidate a JWT before it expires?

Not without additional infrastructure. JWTs are stateless - once issued, they are valid until expiry. To enable early revocation, maintain a blocklist of revoked token IDs (the jti claim) in Redis or your database. This is why short access token expiry (15 minutes) paired with refresh tokens is the recommended pattern.

Scan your codebase for this issue - free

VibeDoctor checks for SEC-016, SEC-001, SEC-014 and 128 other issues across 15 diagnostic areas.

SCAN MY APP →
← Back to all articles View all 129+ checks →