HIGH cryptographic failuresjwt tokens

Cryptographic Failures with Jwt Tokens

How Cryptographic Failures Manifests in Jwt Tokens

Cryptographic failures in JWT tokens often stem from improper key management, algorithm selection, or implementation flaws. One of the most critical vulnerabilities is the none algorithm attack, where a JWT header specifies alg: none but the application still accepts the token without verification. Attackers can craft tokens with arbitrary payloads and bypass authentication entirely.

// Malicious token with none algorithm
{
  "alg": "none",
  "typ": "JWT"
}.{
  "sub": "admin",
  "admin": true
}.

Another common issue is weak key management. Many applications use hardcoded symmetric keys or small key sizes that are vulnerable to brute-force attacks. A 256-bit symmetric key should be used with HS256, but developers often use shorter keys or weak passwords.

// Vulnerable: hardcoded weak key
const jwt = require('jsonwebtoken');
const token = jwt.sign({ userId: 123 }, 'password123', { expiresIn: '1h' });

Algorithm confusion attacks occur when applications accept RS256 tokens but incorrectly validate HS256 tokens. If the public key is used as a symmetric key, attackers can sign tokens with the public key itself, effectively creating valid tokens without the private key.

# Vulnerable: algorithm confusion
import jwt
public_key = open('public.pem').read()
token = jwt.encode({'admin': True}, public_key, algorithm='HS256')

Key leakage through URL parameters or insecure storage is another critical failure. JWTs should never be transmitted in URLs or stored in localStorage without proper safeguards. Additionally, using predictable or sequential JWT IDs (jti) can enable token replay attacks.

// Vulnerable: JWT in URL
app.get('/api/data', (req, res) => {
  const token = req.query.token; // NEVER do this
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  // ...
});

JWT Tokens-Specific Detection

Detecting cryptographic failures in JWT tokens requires both static analysis and dynamic testing. For static analysis, scan your codebase for hardcoded secrets, weak algorithms, and improper JWT handling patterns. Look for alg: none usage, HS256 with weak keys, and algorithm confusion patterns.

# Scan for hardcoded JWT secrets
rg -i "jwt_secret|secret_key|password" --type js --type py
# Find JWT verification patterns
rg -i "jwt\.verify|jwt\.decode" --type js

Dynamic scanning with middleBrick specifically tests JWT cryptographic implementations by attempting algorithm confusion attacks, none algorithm bypass, and key weakness exploitation. The scanner tests unauthenticated endpoints that accept JWTs and verifies proper cryptographic implementation.

CheckmiddleBrick TestRisk Level
None AlgorithmAttempts to authenticate with alg: none tokensCritical
Algorithm ConfusionTests RS256/HS256 confusion with public keyHigh
Weak Key DetectionIdentifies short or predictable symmetric keysHigh
Key LeakageChecks for JWT exposure in URLs, logs, headersMedium

For manual testing, use tools like jwt_tool to analyze token structure and attempt cryptographic attacks:

# Test JWT token for cryptographic weaknesses
jwt_tool eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Monitor JWT-related logs for suspicious patterns: repeated failed verifications, unusual token sizes, or tokens with unexpected algorithms. Implement rate limiting on JWT verification endpoints to prevent brute-force attacks on weak keys.

JWT Tokens-Specific Remediation

Proper JWT cryptographic implementation starts with strong key management. Use environment variables or secure secret management services, never commit keys to version control. For symmetric algorithms, use at least 256-bit keys generated with cryptographically secure random number generators.

// Secure JWT implementation
const crypto = require('crypto');
const jwt = require('jsonwebtoken');

// Generate secure key
const generateKey = () => {
  return crypto.randomBytes(32).toString('base64');
};

// Verify with strict algorithm validation
const verifyToken = (token, secret) => {
  try {
    return jwt.verify(token, secret, {
      algorithms: ['HS256'], // Whitelist only allowed algorithms
      issuer: 'your-domain.com',
      audience: 'your-app'
    });
  } catch (error) {
    // Log and handle specific error types
    console.error('JWT verification failed:', error.name);
    throw new Error('Invalid token');
  }
};

// Sign with proper options
const createToken = (payload) => {
  return jwt.sign(payload, process.env.JWT_SECRET, {
    expiresIn: '1h',
    issuer: 'your-domain.com',
    audience: 'your-app',
    algorithm: 'HS256'
  });
};

For asymmetric algorithms like RS256, ensure proper key pair generation and never expose private keys. Use dedicated JWT libraries that validate algorithm types and reject none algorithms by default.

# Secure RS256 implementation
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives import serialization
import jwt

def generate_rsa_key_pair():
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=2048,
    )
    public_key = private_key.public_key()
    
    # Serialize keys securely
    private_pem = private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.BestAvailableEncryption(b'master-password')
    )
    
    public_pem = public_key.public_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.SubjectPublicKeyInfo
    )
    
    return private_pem, public_pem

def create_jwt_rs256(payload, private_key_pem):
    return jwt.encode(payload, private_key_pem, algorithm='RS256')

def verify_jwt_rs256(token, public_key_pem):
    return jwt.decode(token, public_key_pem, algorithms=['RS256'])

Implement additional security measures: use refresh tokens for long-lived sessions, implement token rotation, and set appropriate expiration times. Never store JWTs in URLs or expose them in client-side logs.

// Refresh token pattern
const refreshAccessToken = (refreshToken) => {
  try {
    const decoded = jwt.decode(refreshToken);
    if (Date.now() > decoded.exp * 1000) {
      throw new Error('Refresh token expired');
    }
    
    // Verify refresh token
    jwt.verify(refreshToken, process.env.REFRESH_SECRET, {
      algorithms: ['HS256']
    });
    
    // Issue new access token
    return createToken({ userId: decoded.userId });
  } catch (error) {
    throw new Error('Invalid refresh token');
  }
};

Frequently Asked Questions

How can I test if my JWT implementation is vulnerable to cryptographic attacks?
Use middleBrick's free scanner to test your JWT endpoints. It automatically attempts none algorithm bypass, algorithm confusion attacks, and weak key detection. You can also use jwt_tool to manually analyze your tokens and attempt cryptographic attacks. Look for tokens that verify with alg: none, tokens that work with public keys as symmetric keys, or tokens that accept weak passwords.
What's the difference between HS256 and RS256 for JWTs, and which should I use?
HS256 uses a shared secret key for both signing and verification, while RS256 uses a private key to sign and a public key to verify. RS256 is generally more secure for distributed systems because private keys never need to be shared. Use RS256 when you have multiple services verifying tokens or when you need to publish public keys. Use HS256 only for single-service applications with proper key management. Never use none, ES* (without proper validation), or allow algorithm confusion.