flawopen.com/Vulnerabilities/JWT Algorithm Confusion
Imagine a nightclub where VIP wristbands are signed in glowing invisible ink. The bouncer uses a special blacklight to check the signature. But an attacker brings a wristband with a label saying: 'Verification method: None needed.' The bouncer reads the label, shrugs, turns off the blacklight, and lets the attacker walk straight into the VIP lounge. In JWT algorithm confusion, the server lets the client dictate which cryptographic formula to use, allowing attackers to forge valid admin tokens without knowing the secret key.
{"alg": "none"} or {"alg": "HS256"} with RSA public keyJWTs contain a header specifying the signing algorithm (e.g. alg: RS256). Flawed libraries trust this header value blindly. In the 'none' attack, the server accepts unsigned tokens. In the HMAC/RSA confusion attack, an attacker changes the algorithm from asymmetric RS256 to symmetric HS256 and signs the token using the server's publicly available RSA public key as the HMAC shared secret.
# VULNERABLE: Letting the incoming token decide which algorithm to use
import jwt
def verify_token(token_str):
# Attacker crafts header: {"alg": "none"}
# Library decodes without checking if 'none' is permitted!
payload = jwt.decode(token_str, options={"verify_signature": False})
return payload
# HARDENED: Pin the allowed algorithms and explicitly enforce signature checks
import jwt
PUBLIC_KEY = open("jwt_public.pem").read()
def verify_token_safe(token_str):
# Strictly enforce RS256: any token with 'none' or 'HS256' is immediately rejected
payload = jwt.decode(
token_str,
PUBLIC_KEY,
algorithms=["RS256"], # Explicitly pin expected algorithm
options={
"verify_signature": True,
"require": ["exp", "iat", "sub"]
}
)
return payload
{"role": "admin"}.{"alg": "none"} and strips the signature part entirely.algorithms=["RS256"] whitelist to your verification function; never rely on defaults.none algorithm in all production environments.exp (expiration) and iss (issuer) claims on every incoming token.