Skip to content
bughra.dev
Go back

JSON Web Token (JWT) Security

Introduction to JWT

JSON Web Tokens (JWTs) are an open standard (RFC 7519) for securely transmitting information between parties as a compact, self-contained JSON object. JWTs are commonly used for authentication and authorization in web applications, API security, single sign-on (SSO) implementations, and information exchange.

JWT Structure

A JWT consists of three parts separated by dots (.):

xxxxx.yyyyy.zzzzz
  1. Header - Contains metadata about the token type and signing algorithm
  2. Payload - Contains the claims (statements about an entity)
  3. Signature - Verifies the token hasn’t been altered

Example JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Common JWT Security Vulnerabilities

1. Algorithm Vulnerabilities

None Algorithm Attack

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

// Modified malicious header
{
  "alg": "none",
  "typ": "JWT"
}

The none algorithm indicates no signature verification is needed, potentially allowing attackers to forge tokens if accepted by the server.

Algorithm Confusion/Key Confusion Attack

// Original header (symmetric algorithm)
{
  "alg": "HS256",
  "typ": "JWT"
}

// Modified to asymmetric algorithm
{
  "alg": "RS256",
  "typ": "JWT"
}

This attack exploits implementations that don’t validate the algorithm. An attacker might switch from RS256 (asymmetric) to HS256 (symmetric) and use the public key as the HMAC secret.

2. Weak Secret Keys

Using weak, short, or predictable secrets for HMAC-based tokens enables brute force attacks:

# Example brute force attack using hashcat
hashcat -m 16500 -a 0 "JWT_TOKEN" wordlist.txt

3. Missing Signature Validation

Some implementations might:

4. Token Information Disclosure

JWTs store information in base64url-encoded format, not encrypted by default:

// Decoding a JWT payload (can be done by anyone)
const payload = atob(token.split('.')[1]);
console.log(JSON.parse(payload)); // All claims are visible

5. Token Replay Attacks

Without proper validation mechanisms, a token can be intercepted and reused:

// Captured valid token
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

6. Missing Expiration Time

Tokens without expiration remain valid indefinitely:

// JWT payload without expiration
{
  "sub": "1234567890",
  "name": "John Doe",
  "admin": true
}

7. JWT Header Injection

// Maliciously constructed header
{
  "alg": "HS256",
  "typ": "JWT",
  "cty": "nested-jwt",
  "x5u": "http://attacker.com/key.pem"
}

Some implementations might fetch keys from the URL in x5u.

Best Practices for JWT Security

1. Proper Algorithm Selection and Validation

// Node.js example with explicit algorithm check
jwt.verify(token, secretKey, { algorithms: ['HS256'] });

2. Strong Secret Keys

# Generate a strong random key (32 bytes = 256 bits)
openssl rand -hex 32

3. Implement Token Expiration

// JWT payload with expiration
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,
  "exp": 1516242622  // 1 hour after issued time
}

4. Use Additional Validation Claims

// Enhanced JWT payload
{
  "sub": "1234567890",
  "name": "John Doe",
  "iat": 1516239022,  // Issued at timestamp
  "exp": 1516242622,  // Expiration time
  "nbf": 1516239022,  // Not valid before
  "jti": "unique-token-id-123",  // Unique identifier
  "aud": "https://api.example.com"  // Intended audience
}

5. Token Revocation Mechanisms

Options include:

6. Secure Token Storage on Clients

// Storing JWT in httpOnly cookie (browser-side)
document.cookie = "token=your_jwt_token; HttpOnly; Secure; SameSite=Strict";

7. Protect Against XSS and CSRF

8. Consider Encrypted JWTs (JWE)

# Example using jose CLI tool
jose encrypt --input="jwt.txt" --recipient=public_key.pem > encrypted_jwt.txt

JWT Testing Checklist

Tools for JWT Testing and Security

Analysis and Debugging

Attack Tools

python3 jwt_tool.py eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Library-specific Security

Node.js

const jwt = require('jsonwebtoken');

// Secure token creation
const token = jwt.sign(
  { sub: userId, role: 'user' },
  process.env.JWT_SECRET,
  { 
    algorithm: 'HS256',
    expiresIn: '1h',
    notBefore: '0s',
    audience: 'https://api.example.com',
    issuer: 'https://auth.example.com'
  }
);

// Secure verification
try {
  const decoded = jwt.verify(token, process.env.JWT_SECRET, { 
    algorithms: ['HS256'],
    audience: 'https://api.example.com',
    issuer: 'https://auth.example.com'
  });
} catch (err) {
  // Handle invalid tokens
}

Python

import jwt
from datetime import datetime, timedelta

# Secure token creation
payload = {
    'sub': user_id,
    'role': 'user',
    'iat': datetime.utcnow(),
    'exp': datetime.utcnow() + timedelta(hours=1),
    'aud': 'https://api.example.com',
    'iss': 'https://auth.example.com'
}

token = jwt.encode(payload, os.environ.get('JWT_SECRET'), algorithm='HS256')

# Secure verification
try:
    decoded = jwt.decode(
        token, 
        os.environ.get('JWT_SECRET'), 
        algorithms=['HS256'],
        audience='https://api.example.com',
        issuer='https://auth.example.com'
    )
except jwt.InvalidTokenError:
    # Handle invalid tokens

Real-world JWT Vulnerabilities

  1. CVE-2018-0114: Node.js node-jose library vulnerability allowed algorithm confusion attacks
  2. CVE-2020-28042: Ruby JWT gem bypassing signature verification with trailing data
  3. CVE-2022-21449: Java’s ECDSA signature validation vulnerability allowing crafted JWTs with blank signatures
  4. CVE-2022-29156: Spring Security accepting JWTs with invalid signatures due to parser issues

Common JWT Attack Scenarios

Scenario 1: Extracting Sensitive Information

1. Capture JWT from authorization header or cookies
2. Base64-decode the payload section
3. Extract sensitive information (emails, roles, permissions)

Scenario 2: Altering JWT Claims

1. Decode the existing JWT
2. Modify payload (e.g., change 'role': 'user' to 'role': 'admin')
3. Remove signature or attempt to bypass signature verification
4. Re-encode the tampered JWT
5. Use in authentication/authorization context

Scenario 3: Key Theft and Forgery

1. Identify JWT signing key location
2. Target vulnerabilities to extract the key (e.g., SSRF, path traversal)
3. Use the obtained key to forge valid tokens
4. Leverage forged tokens to access protected resources

Conclusion

JWT security requires a combination of:

By following these best practices and security measures, organizations can leverage the benefits of JWTs while minimizing the associated security risks.


Share this post on:

Previous Post
Cross-Site Request Forgery (CSRF)
Next Post
Local File Inclusion (LFI) & Path Traversal