JWT Security Best Practices
Common JWT implementation mistakes and how to avoid them. Plus, how to use RaSEC's JWT Analyzer.

JSON Web Tokens have become the default authentication mechanism for modern applications. They're stateless, which means servers don't need to store session data. They're portable, which means they work across services. They're standardized, which means libraries exist for every language and framework.
They're also frequently implemented in ways that completely undermine security. JWT vulnerabilities appear in production systems constantly, often providing complete authentication bypass. Understanding these vulnerabilities—and knowing how to find them—is essential for anyone testing modern applications.
Understanding JWT Structure
A JWT consists of three base64-encoded parts separated by dots: header, payload, and signature. The header specifies the algorithm used for signing. The payload contains claims about the authenticated user. The signature proves the token wasn't tampered with.
The critical thing is that the header and payload are just base64 encoded, not encrypted. Anyone can read them. The signature is what provides security—it proves the token was created by someone with the signing key.
When that signing process fails—due to algorithm confusion, weak keys, or implementation bugs—the entire authentication system collapses.
Algorithm Confusion Attacks
The most famous JWT vulnerability involves the "alg" header. This header tells the server which algorithm was used to sign the token. The server is supposed to know what algorithm it expects and enforce that expectation. Many servers don't.
The "none" algorithm is a legitimate part of the JWT spec, intended for cases where tokens are passed through trusted channels that don't require signatures. If a server accepts tokens with "alg": "none" without validation, attackers can forge tokens without any signing key.
Even more dangerous is the RS256/HS256 confusion attack. RS256 uses asymmetric cryptography: the server has a private key for signing and a public key for verification. HS256 uses symmetric cryptography: the same secret signs and verifies.
If an attacker knows the server's RS256 public key (often available in /.well-known/jwks.json or just embedded in client-side code), they can forge tokens by:
- Changing the algorithm header from RS256 to HS256
- Signing the token with the public key using HMAC
If the server blindly trusts the algorithm header and uses the "key" (now the public key) for HMAC verification, the forged token validates. The attacker has effectively turned the public key into a shared secret.
Weak Keys and Key Management
Even with proper algorithm enforcement, JWT security depends on key strength. JWT implementations using HS256 require a secret key for signing. If that key is short, predictable, or leaked, tokens can be forged.
Common weak key patterns include: default keys left over from development ("secret", "key", "changeme"), short keys that can be brute forced, keys stored in client-side code or public repositories, and keys derived from predictable values like application names.
Tools like jwt_tool and hashcat can attempt to crack JWT signing keys. Against a weak 8-character password, this takes seconds. Against a proper 256-bit random key, it's computationally infeasible.
Key rotation is equally important but often neglected. When keys never change, if they're ever compromised, the attacker has persistent access. Proper implementations rotate keys regularly and maintain a key ID (kid) header to identify which key signed which token.
Implementation Bugs in Libraries
JWT libraries have had serious vulnerabilities. The algorithm confusion issue was widespread because libraries tried to be helpful by accepting whatever algorithm was specified rather than enforcing expected algorithms.
Other library bugs have included:
- Accepting tokens with no signature at all (not just "alg": "none")
- Integer overflow in expiration checks allowing expired tokens
- Unicode normalization issues allowing claim manipulation
- Signature verification using incorrect string comparison
Staying current with library patches and using well-maintained libraries for your language significantly reduces these risks. But when testing applications, assume the worst and check for known JWT library vulnerabilities.
Claim Violations
Even with proper signing, JWT claims can be problematic. The "exp" claim specifies when a token expires. Some applications don't check it. Others compare against the wrong timezone. Testing expired tokens often reveals these issues.
The "aud" (audience) claim specifies which services should accept the token. In microservice environments, a token intended for one service might be accepted by another if audience validation is missing. This can escalate access when services have different privilege levels.
User identification claims (sub, user_id, etc.) are sometimes confused or improperly validated. Occasionally changing these values in a self-signed or manipulated token reveals that the server doesn't properly verify signatures before trusting claim values.
Testing JWT Security
When testing JWT implementations:
Examine the token structure. Decode the base64 parts and understand what claims are present. Note the algorithm and any key identifiers.
Try algorithm substitution. Change RS256 to HS256 and sign with the public key. Change any algorithm to "none" and remove the signature. Send tokens with header specifying unusual or nonexistent algorithms.
Test key strength if using symmetric algorithms. Attempt brute force against common passwords and default keys. Check documentation and source code for hardcoded keys.
Manipulate claims. Change user identifiers, roles, and permissions. Use an admin's user ID with your token. Add admin: true or similar claims.
Test expiration handling. Send expired tokens. Send tokens with expiration set to extreme values. Send tokens without expiration claims.
Look for key endpoints. Check /.well-known/jwks.json, /oauth/certs, and similar paths for exposed public keys.
Secure JWT Implementation
Defending JWT requires attention to detail:
Explicitly specify expected algorithms in token verification. Never let the token header dictate which algorithm to use for verification.
Use strong, random keys. For HS256, use at least 256 bits of entropy. For RS256, use at least 2048-bit keys. Store keys securely, never in code or public locations.
Validate all critical claims. Check expiration, audience, and issuer. Compare against expected values, don't just verify they exist.
Keep libraries updated. JWT library vulnerabilities have been severe and widespread.
Implement token rotation and key rotation. Limit token lifetime to reduce exposure if tokens are stolen. Rotate signing keys regularly.
Consider token binding where sensitive operations require additional verification, not just a valid JWT.
RaSEC JWT Analyzer
RaSEC's JWT Analyzer automates much of this testing. It decodes tokens, identifies algorithm configuration, tests for common vulnerabilities, and attempts key cracking against weak secrets.
But beyond automated testing, it helps you understand what's happening in your JWT implementation. Security requires knowledge, not just tools. Understanding how JWTs work—and fail—makes you better at both testing and building secure applications.