A JWT carries identity/permission claims, protected by a signature - so if the server verifies it wrongly, an attacker forges a token asserting any identity (auth bypass, account takeover). The classic attacks: alg:none (accept unsigned tokens), algorithm confusion (RS256→HS256, signing with the public key as HMAC secret), weak signing secret (brute-forceable HMAC key), unverified signature (server never checks), and kid injection (manipulating the key-id header). Impact is critical because JWTs front authentication. The fixes: enforce a specific expected algorithm (reject none, never let the header pick), strong secrets/keys, always verify before trusting claims, validate expiry/issuer/audience, short lifetimes + revocation. Detail below; part of every auth/SSO test.
// 01 Why JWTs are a high-value target
A JSON Web Token is a compact, self-contained token that carries claims - typically a user's identity and permissions - between parties, most often for authentication and session management. It has three parts: a header (including the signing algorithm), a payload of claims, and a signature that's supposed to prove the token hasn't been tampered with. Here's why it's a target: whoever holds a valid JWT is treated as that user, and the entire security model rests on the server correctly verifying the signature before trusting the claims. If that verification is weak, wrong, or missing, an attacker can forge a token asserting any identity or privilege they choose - flipping "admin": false to true, or changing the user ID - and walk in. That's authentication bypass and account takeover from editing a token, which is why JWT flaws rate critical.
// 02 The classic attacks
alg:none
Set the header algorithm to none, drop the signature. If the server accepts it, the forged token is “valid” unsigned.
Algorithm confusion
Switch RS256→HS256 and sign with the app's public key as the HMAC secret - a validly-signed forgery.
Weak secret
An HMAC key that's short or a dictionary word can be brute-forced offline, then used to sign any token.
Unverified / kid injection
The server never verifies the signature; or the kid header is manipulated to point at an attacker-chosen key.
Every one exploits the same root cause: the server trusting the token's own header or claims about how to verify it, instead of enforcing what it expects. The token is attacker-controlled input - treating any part of it as trusted before verification is the mistake.
// 03 alg:none & algorithm confusion, in detail
These two deserve a closer look because they're the most impactful. alg:none: JWTs declare their algorithm in the header, and one historically-permitted value is none - meaning “unsigned.” The attacker takes a valid token, edits the payload (new identity, elevated role), sets the header to alg:none, and removes the signature. A server that accepts none - because it doesn't enforce an expected algorithm - treats the forged, unsigned token as genuine. Trivial forgery. Algorithm confusion: when an app supports both asymmetric (RS256) and symmetric (HS256) signing and picks based on the header, an attacker sets the header to HS256 and signs the token using the app's public RSA key as the HMAC secret. If the server, seeing HS256, verifies with that public key as a symmetric secret, the signature checks out - because the public key is public, the attacker had it all along. The lesson for both: verification must be bound to the algorithm and key the server expects, never dictated by the token.
// 04 How to test for JWT flaws
JWT testing is systematic. First, decode the token (the header and payload are just base64 - not encrypted) to understand the claims and algorithm. Then attempt each attack: tamper with the payload and see if the unmodified server accepts it (unverified signature); set alg:none and strip the signature; try algorithm confusion if both RS/HS are plausibly supported; brute-force a weak HMAC secret offline against a captured token; and probe the kid and other headers for injection. Also check the claims logic: are exp (expiry), iss and aud validated? And crucially - because JWTs are stateless - test revocation: does a token still work after logout or password change? These are manual checks that require understanding the token model, and they're a core part of every authentication and SSO assessment.
// 05 Using JWTs safely
The fixes are well-defined. Enforce a specific expected algorithm on verification and reject all others - especially none; never let the token header dictate the verification algorithm or key (this kills both alg:none and algorithm confusion). Use strong, high-entropy signing secrets for HMAC, or properly managed keys for asymmetric signing, and never expose the signing secret. Always verify the signature before trusting any claim, and validate exp, iss and aud. Keep token lifetimes short and implement a revocation or short-lifetime-plus-refresh strategy, since a stateless JWT can't easily be invalidated once issued - the session-handling problem in token form. And keep JWT libraries patched: several of these attacks were enabled by permissive library defaults that have since been hardened. Together these turn a fragile token into a trustworthy one - the standard our methodology reports against.
// 06 Frequently asked questions
What is a JWT and why is it a target?
A JSON Web Token represents claims - commonly identity and permissions - for authentication and session management. It has a header, a payload of claims, and a signature that's supposed to prove it wasn't tampered with. It's a target because whoever holds a valid one is treated as that user, and security depends entirely on the server correctly verifying the signature. Weak or missing verification lets an attacker forge a token asserting any identity - auth bypass and account takeover.
What is the alg:none attack?
JWTs specify their algorithm in the header, and one historic value is 'none' (unsigned). The attacker edits the payload to assert a different identity or privileges, sets the header to 'none', and removes the signature. A server that accepts 'none' - because it doesn't enforce an expected algorithm - treats the forged, unsigned token as valid. The fix is to reject 'none' and enforce a specific expected algorithm.
What is algorithm confusion?
It exploits an app supporting both asymmetric (RS256) and symmetric (HS256) signing that picks based on the header. The attacker sets the header to HS256 and signs the token with the app's public RSA key as the HMAC secret. If the server verifies HS256 using the public key as a symmetric secret, the signature checks out - because the public key is known. The fix is binding verification to the expected algorithm and key type, not the header.
How do you use JWTs securely?
Enforce a specific expected algorithm and reject others (especially 'none'); never let the header dictate verification. Use strong secrets or managed keys and never expose them. Always verify the signature before trusting claims, and validate expiry, issuer and audience. Keep lifetimes short with a revocation or short-lifetime-plus-refresh strategy, since a stateless JWT can't easily be invalidated. Keep libraries patched.
// 07 Related reading
- Session management testing checklist — JWTs in the wider session context.
- OAuth & SSO security testing and OWASP API Security Top 10.
- API and web application penetration testing.