How to Bulletproof JWTs: Prevent Token Theft, None Attacks, and Brute‑Force
This article examines common JWT vulnerabilities—including token exposure via localStorage, algorithm‑tampering “none” attacks, weak signing keys, and lack of revocation—and presents a robust solution using HTTPS transmission, HttpOnly Secure cookies, SM9 cryptographic signatures, and a Redis‑based blacklist to achieve dramatically improved security.
Hello, I am your friend Architecture Jun, a architect who writes code and poetry.
Last week a startup colleague, Old Wang, rushed into my studio with dark circles under his eyes, shouting that his newly launched medical appointment mini‑program had been compromised: a user’s data was leaked, a stolen JWT token allowed attackers to view VIP patients’ records and even modify appointment times. The breach stemmed from several JWT weaknesses.
Pain Point 1: Token Runs Naked in localStorage
Storing JWTs directly in localStorage exposes them to XSS attacks, effectively leaving the token in a public window where any script can steal it, just like hanging your house key on the front door.
Lesson: Sensitive data must never be stored in an easily accessible place.
Pain Point 2: Algorithm Tampering – the “None” Attack
The JWT header’s alg field tells the server which algorithm to use for verification. An attacker can change this value to none, causing a server that does not strictly validate the field to accept an unsigned token, granting arbitrary access.
Lesson: Servers must enforce algorithm verification, signature, and expiration checks.
Pain Point 3: Weak Signing Keys – Brute‑Force Risk
If the signing key is short, common, or never rotated, attackers can use high‑performance GPUs to brute‑force the key, similar to cracking a simple three‑digit lock.
Lesson: Use strong, long (at least 256‑bit) keys and rotate them regularly.
Pain Point 4: Tokens Never Expire – No Revocation
Because JWTs are stateless, once issued they cannot be invalidated by the server until they naturally expire. If a user changes a password, loses a device, or is kicked out, the stolen token remains valid.
Lesson: Statelessness is a double‑edged sword; rely solely on expiration creates a time bomb for sensitive systems.
Black‑Tech Solution: JWT + Redis Blacklist (500% Security Boost)
The core idea is to regain control over JWTs by adding a revocation layer.
Token Dress‑Up: No Naked Transmission
Transport: Enforce full‑time HTTPS.
Secure Storage
Replace localStorage with HttpOnly + Secure cookies, which are inaccessible to JavaScript and only sent over HTTPS.
Signature Hardening
Use the Chinese national standard SM9 asymmetric algorithm instead of common algorithms like HS256, providing strong resistance to brute‑force and algorithm‑tampering attacks.
Redis Blacklist for Revocation
Deploy a high‑performance Redis instance.
When a user logs out, changes password, or is forced offline, immediately write the token’s unique identifier ( jti) or user‑ID + fingerprint into Redis with a TTL slightly longer than the token’s remaining lifetime.
On each request, after normal signature and expiration checks, query Redis; if the token is found in the blacklist, reject the request with 401 Unauthorized.
Effectiveness comparison:
Attack Vector
Plain JWT
JWT + Redis Blacklist + SM9
Improvement
Token theft (XSS)
Very high risk (login possible)
Medium risk (requires cookie theft and revocation)
↑ Security
Algorithm tampering (None)
High risk (privilege escalation)
Very low risk (SM9 signature cannot be altered)
↑↑↑ Significant boost
Key brute‑force
Medium‑high risk (depends on key strength)
Very low risk (SM9 resists exhaustive attacks)
↑↑↑ Significant boost
Post‑leak token use
High risk (full validity)
Very low risk (blacklist invalidates instantly)
↑↑↑↑↑ 500%+ improvement
The 500% figure reflects the regained controllability: previously a stolen token could roam freely until expiration; with Redis blacklist it can be disabled within seconds, dramatically shrinking the attack window, which is critical for finance, healthcare, and government systems.
Sample Code (Python + Flask)
from flask import request, jsonify
import jwt # library that supports SM9 or national‑crypto extensions
import redis
import time
# Configure Redis connection
redis_client = redis.Redis(host='localhost', port=6379, db=0)
# Middleware: verify JWT
def jwt_required(func):
def wrapper(*args, **kwargs):
# 1. Get token from HttpOnly cookie
token = request.cookies.get('access_token')
if not token:
return jsonify({"error": "Missing token"}), 401
try:
# 2. Verify signature with SM9 public key (pseudo‑function)
payload = validate_sm9_jwt(token) # returns payload or raises
# 3. Check expiration
if payload['exp'] < time.time():
return jsonify({"error": "Token expired"}), 401
# 4. Blacklist check
jti = payload['jti']
if redis_client.exists(f"jwt_blacklist:{jti}"):
return jsonify({"error": "Token revoked"}), 401
# Attach payload for downstream use
request.user_payload = payload
return func(*args, **kwargs)
except jwt.InvalidTokenError:
return jsonify({"error": "Invalid token"}), 401
return wrapper
# Logout endpoint – add token to blacklist
@app.route('/logout', methods=['POST'])
@jwt_required
def logout():
current_jti = request.user_payload['jti']
token_exp = request.user_payload['exp']
time_left = token_exp - time.time()
# Store in Redis with TTL slightly longer than remaining life
redis_client.setex(f"jwt_blacklist:{current_jti}", int(time_left) + 60, "revoked")
return jsonify({"msg": "Logout successful"})Conclusion: JWTs are convenient but unsafe when used “naked.” By combining SM9 strong signatures, HTTPS transmission, HttpOnly Secure cookies, and a Redis‑backed blacklist, you give JWTs a “bulletproof vest” and a remote self‑destruct mechanism, dramatically improving security for any system that demands strong protection.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
