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.
| Check | middleBrick Test | Risk Level |
|---|---|---|
| None Algorithm | Attempts to authenticate with alg: none tokens | Critical |
| Algorithm Confusion | Tests RS256/HS256 confusion with public key | High |
| Weak Key Detection | Identifies short or predictable symmetric keys | High |
| Key Leakage | Checks for JWT exposure in URLs, logs, headers | Medium |
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');
}
};