← Back to Learn

Common JWT Vulnerabilities

Understanding common vulnerabilities in JWT implementations is crucial for building secure applications. For comprehensive vulnerability scanning and management, consider using tools like Tenable or Bitdefender.

1. Algorithm "none" Vulnerability

CRITICAL

If a server accepts tokens with the "none" algorithm, attackers can modify tokens without a valid signature.

Vulnerable Token
// Vulnerable token
{
  "alg": "none",
  "typ": "JWT"
}

// Attacker can modify payload without signature
// Server accepts it because algorithm is "none"

Fix: Always explicitly specify allowed algorithms during verification.

2. Algorithm Confusion Attack

HIGH

If a server expects RS256 but accepts HS256, an attacker can forge tokens using the public key as a secret.

Attack Scenario
// Server expects RS256 (asymmetric)
// But accepts HS256 (symmetric) if not validated

// Attacker:
// 1. Gets public key (often exposed in JWKS endpoint)
// 2. Changes algorithm to HS256
// 3. Signs token with public key as secret
// 4. Server verifies with public key as secret - SUCCESS!

Fix: Always verify the algorithm matches what you expect.

3. Weak Secret Key

HIGH

Using weak or default secrets makes tokens vulnerable to brute force attacks.

Weak Secrets
// Weak secrets
secret = "secret"
secret = "password"
secret = "123456"

// Attacker can brute force these easily

Fix: Use strong, randomly generated secrets (at least 32 characters).

4. No Expiration

MEDIUM

Tokens without expiration never expire, allowing indefinite access if compromised.

Fix: Always set expiration times. Use refresh tokens for long-term access.

5. Sensitive Data in Payload

HIGH

JWTs are base64 encoded, not encrypted. Anyone can decode and read the payload.

Bad vs Good
// NEVER do this
{
  "sub": "user123",
  "password": "plaintext_password",  // ❌
  "credit_card": "1234-5678-9012",  // ❌
  "ssn": "123-45-6789"              // ❌
}

// Only include non-sensitive data
{
  "sub": "user123",
  "name": "John Doe",
  "role": "admin"
}

6. Key ID (kid) Header Injection

MEDIUM

If the "kid" header is not properly validated, attackers can point to malicious keys.

Fix: Validate "kid" against a whitelist of trusted keys.

7. JWT Header Injection (kid, jku, x5u)

CRITICAL

Header parameters like "kid" (Key ID), "jku" (JWK Set URL), and "x5u" (X.509 URL) can be manipulated to point to attacker-controlled keys.

Header Injection Attack
// Vulnerable header
{
  "alg": "RS256",
  "kid": "../../../etc/passwd",  // Path traversal
  "jku": "https://attacker.com/keys.json",  // External key source
  "x5u": "https://evil.com/cert.pem"  // Malicious certificate
}

// Attack scenarios:
// 1. kid injection: Point to a predictable or weak key
// 2. jku spoofing: Redirect to attacker's JWKS endpoint
// 3. x5u manipulation: Use attacker's certificate

Fix: Whitelist allowed kid values, validate jku URLs against trusted domains, and verify certificate chains.

8. JWKS Spoofing Attack

CRITICAL

If the server fetches keys from a JWKS endpoint specified in the token, attackers can redirect to their own endpoint.

JWKS Spoofing
// Attacker creates token with malicious jku
{
  "alg": "RS256",
  "jku": "https://attacker.com/.well-known/jwks.json"
}

// Attacker hosts their own JWKS endpoint with their private key
// Server fetches the key and verifies the token - SUCCESS!

// Prevention:
// - Only allow JWKS from trusted domains
// - Use CORS and certificate pinning
// - Cache JWKS responses

Fix: Whitelist trusted JWKS endpoints, implement certificate pinning, and cache JWKS responses.

9. Key Confusion (RS256 → HS256)

CRITICAL

If a server expects RS256 (asymmetric) but accepts HS256 (symmetric), an attacker can use the public key as a secret to forge tokens.

Key Confusion Attack
// Server expects RS256, but doesn't validate algorithm
// Attacker:
// 1. Obtains public key (often from /.well-known/jwks.json)
// 2. Changes algorithm from RS256 to HS256
// 3. Signs token with public key as HMAC secret
// 4. Server verifies with public key as secret - accepts!

// Vulnerable verification:
jwt.verify(token, publicKey);  // ❌ Doesn't check algorithm

// Secure verification:
jwt.verify(token, publicKey, { algorithms: ['RS256'] });  // ✅

Fix: Always explicitly specify allowed algorithms in verification. Never accept HS256 when expecting RS256.

10. Claim Injection & JWT Replay

HIGH

If claims are not properly validated, attackers can inject malicious claims or replay old tokens.

Claim Injection
// Claim injection
{
  "sub": "user123",
  "role": "admin",  // Injected claim
  "isAdmin": true,  // Privilege escalation
  "permissions": ["*"]  // Overly permissive
}

// Replay attack:
// 1. Attacker captures a valid token
// 2. Token is expired but server doesn't check
// 3. Attacker replays the token indefinitely

// Prevention:
// - Validate all claims
// - Check jti (JWT ID) for replay prevention
// - Use short expiration times
// - Implement token blacklisting

Fix: Validate all claims, use jti for replay prevention, implement token blacklisting, and check expiration.

11. Subdomain Takeover via JWKS

HIGH

If a subdomain used for JWKS is not properly secured, attackers can take it over and serve malicious keys.

Subdomain Takeover
// Scenario:
// 1. Token references jku: "https://keys.example.com/.well-known/jwks.json"
// 2. Subdomain "keys.example.com" is not properly secured
// 3. Attacker takes over subdomain (expired DNS, misconfigured hosting)
// 4. Attacker hosts their own JWKS with their private key
// 5. Server accepts tokens signed with attacker's key

// Prevention:
// - Secure all subdomains
// - Use certificate pinning
// - Monitor subdomain status
// - Whitelist specific JWKS endpoints

Fix: Secure all subdomains, implement certificate pinning, and whitelist trusted JWKS endpoints.

12. Timing Attacks on Signature Verification

MEDIUM

If signature verification uses string comparison instead of constant-time comparison, attackers can use timing differences to extract secrets.

Timing Attack
// Vulnerable (timing attack possible):
if (signature === expectedSignature) {  // ❌
  return true;
}

// Secure (constant-time comparison):
if (crypto.timingSafeEqual(signature, expectedSignature)) {  // ✅
  return true;
}

// Attack:
// Attacker measures response time for different signatures
// Timing differences reveal information about the secret

Fix: Use constant-time comparison functions (e.g., crypto.timingSafeEqual) for signature verification.

13. XSS and Token Theft

CRITICAL

Storing tokens in localStorage makes them vulnerable to XSS attacks.

Fix: Use HttpOnly cookies instead of localStorage.

🔍

Tenable - Vulnerability Management (10% Discount)

10% OFF

Get 10% off Tenable vulnerability scanning and management solutions. Identify and remediate security vulnerabilities in your infrastructure.

Learn More

Affiliate Link

🔐

Tenable One - Unified Exposure Management Platform

Platform

The world's leading AI-powered exposure management platform. Gain visibility across your entire attack surface, prioritize risks, and close exposure gaps across cloud, vulnerability, AI, OT/IoT, and identity domains.

Learn More

Affiliate Link