JSON Web Tokens (JWTs) are the backbone of authentication in modern web applications and APIs. They're elegant: a self-contained, cryptographically signed token that encodes user identity and permissions. But their flexibility is also their vulnerability. The JWT specification contains design decisions that, when combined with common implementation mistakes, create authentication bypasses that let attackers forge tokens and impersonate any user.
The Algorithm Confusion Attack
JWTs support multiple signing algorithms. The two most common are HS256 (symmetric, using a shared secret) and RS256 (asymmetric, using a public/private key pair). The token's header declares which algorithm was used.
Here's the vulnerability: if a server uses RS256 (asymmetric), an attacker can:
- Obtain the server's public key (which is, by definition, public)
- Create a forged JWT with the algorithm set to
HS256 - Sign the token using the public key as the HS256 shared secret
If the server's JWT library trusts the algorithm specified in the token header without verifying it matches the expected algorithm, it will verify the HS256 signature using the public key — and the forged token passes validation. The attacker can now create tokens with any identity and any permissions.
The "none" Algorithm
The JWT specification includes an alg: "none" option for unsigned tokens. This was intended for tokens that are integrity-protected by other means. In practice, many JWT libraries accept unsigned tokens if the header specifies "alg": "none". The attacker removes the signature entirely, and the token is accepted.
// Forged token header (base64 decoded)
{
"alg": "none",
"typ": "JWT"
}
// Payload with arbitrary claims
{
"sub": "admin",
"role": "superuser",
"iat": 1700000000
}
This bypass is trivial — no cryptographic knowledge required, no keys to obtain. Just modify the header and delete the signature.
Weak Signing Secrets
When using HS256, the security of every token depends on the strength of the shared secret. If the secret is weak — a dictionary word, a short string, or a default value — an attacker can brute-force it offline. JWT cracking tools can test millions of candidate secrets per second against a captured token.
Common weak secrets found in the wild include: secret, password, your-256-bit-secret (the example from jwt.io), application names, and UUIDs that were generated once and never rotated.
Missing Expiration Validation
JWTs can include an exp (expiration) claim, but:
- The claim is optional — tokens without
expnever expire - The server must actively validate the claim — many implementations don't
- Clock skew tolerance can be set too high, extending token validity
A stolen token without expiration (or with lax expiration enforcement) is a permanent credential. Unlike server-side sessions that can be invalidated, a JWT is valid as long as the signature checks out and the expiration hasn't passed.
The Stateless Revocation Problem
JWTs' stateless nature — their biggest architectural advantage — creates their biggest security challenge: revocation. If a user's account is compromised, their password is changed, or their permissions are downgraded, existing JWTs remain valid until expiration. There's no server-side session to delete.
Organisations that issue long-lived JWTs (hours or days) without a revocation mechanism have a window where compromised tokens remain usable despite the compromise being detected.
Claims Injection and Privilege Escalation
JWTs often encode authorization decisions in their claims: "role": "user", "permissions": ["read"], "admin": false. If any of the above vulnerabilities allow token forging, the attacker doesn't just gain access — they gain access as anyone with any permission level. The authorization model collapses entirely.
ShieldReport assesses your application's external security posture, including the authentication infrastructure that protects tokens in transit — because a perfectly implemented JWT is still compromised if it's transmitted over weak TLS or stored in cookies without security attributes.