JWT (JSON Web Token) Security: Attacks and Defense
A JWT is a portable identity token signed by the server; if misconfigured, an attacker can craft their own. alg none, missing signature checks, key confusion and weak key attacks, the logout problem and a secure JWT checklist.
Quick answer: A JWT (JSON Web Token) is a portable identity token that carries a user's identity and permissions, signed and verified by the server. In modern web and mobile apps, JWT has largely replaced session management. But if a JWT is misconfigured, an attacker can craft their own token and take over another account or admin rights. The best known flaws are: accepting an unsigned token (alg: none), a weak or leaked signing key, never verifying the signature and not checking the token's expiry. Secure JWT use rests on three things: always verify the signature, use a strong secret key, and check expiry and scope claims on the server.
JWT is a powerful tool but comes with a dangerous assumption: "if the token is signed, it is trusted." The problem is whether the signature is actually verified. In many real incidents the server reads the token's contents but does not check its signature or checks it wrong. This article explains how JWT works, the most common attacks, and proper defense in the context of API security.
How JWT works
A JWT has three parts separated by dots:
- Header: Says which signing algorithm is used.
- Payload: Carries data like user id, permissions and expiry. It is not encrypted, only encoded; anyone can read it.
- Signature: The header and payload signed with the server's secret key. If the token is altered, the signature no longer matches.
Critical misconception: the payload is not encrypted. Never put sensitive data like passwords or card numbers in a JWT. JWT provides trust through the signature, not confidentiality.
Most common JWT attacks
| Attack | Root cause | Result |
|---|---|---|
| alg: none acceptance | Server accepts unsigned token | Anyone forges a valid token |
| No signature check | Payload read, signature ignored | Token freely altered |
| Weak HMAC key | Guessable secret key | Key cracked, tokens forged |
| Key confusion | HS256 accepted when RS256 expected | Forged signature with public key |
| No expiry check | exp claim not checked | Stolen token valid forever |
alg: none
Some libraries accept a token as unsigned if the header algorithm says "none." The attacker edits the payload freely, leaves the signature empty and becomes admin. The server must decide itself which algorithm to accept, not trust what the token says.
Key confusion (RS256 and HS256)
RS256 uses a public and private key pair; it signs with the private key and verifies with the public key. The attacker tricks the server into HS256 and uses the publicly available public key as the HMAC secret to forge a valid signature. Defense: the server must accept only the expected algorithm.
Weak key
In symmetric signatures like HS256 the key is a password. Weak keys like "secret" or "123456" are cracked by dictionary attacks, the domain of password cracking tools. The key must be long, random, secret and not embedded in code.
The logout problem in JWT
In classic sessions logging a user out is easy: delete the server session. JWT is stateless; the server does not remember the token. Once issued, a JWT is valid until it expires. If stolen, it is hard to revoke. Solutions:
- Short expiry. The access token should be short lived (minutes), the refresh token separate and better protected.
- Blacklist. Keeping revoked tokens in a list, but this reduces the benefit of statelessness.
- Token rotation. The refresh token should change on every use, and reuse should be detected.
Secure JWT checklist
- The signature is verified on every request, never skipped.
- The server fixes the algorithm it accepts; it does not trust the token.
- The secret key is strong, random and in an environment variable, not in code. As in our leaked API keys article, it must not leak to the repo.
- exp (expiry) and if needed aud, iss claims are checked.
- No sensitive data in the payload.
- Access token short, refresh token protected and rotated.
- If possible keep the token in a cookie with SameSite or in secure storage; consider CSRF risk.
Frequently asked questions
Is a JWT encrypted? No. A default JWT is signed but not encrypted. The payload is readable by anyone. If confidentiality is needed, use JWE or transport layer encryption.
What happens if a JWT is stolen? The attacker can use it until it expires. That is why short expiry, secure transport and refresh token rotation matter.
Is storing a JWT in localStorage safe? localStorage is exposed to XSS; an XSS flaw can steal the token. A SameSite cookie may be safer, but with CSRF measures. The right choice depends on the app's threat model.
Is JWT always right for session management? No. In scenarios needing instant revocation, single server or high security, a classic server side session may fit better. JWT favors scale and statelessness.
Sources
- OWASP, JSON Web Token Cheat Sheet: https://cheatsheetseries.owasp.org
- OWASP API Security Top 10: https://owasp.org/API-Security
- RFC 7519, JSON Web Token: https://datatracker.ietf.org/doc/html/rfc7519
- PortSwigger Web Security Academy, JWT attacks: https://portswigger.net/web-security/jwt
To test the JWT configuration in your API and application for alg none, key confusion and signature verification, contact DSET. From our Ankara Hacettepe Teknokent laboratory we provide API security and penetration testing.
Kimliğinizi doğrulayın
Yetkilendirilmiş erişim alanı. Tüm giriş denemeleri kayıt altına alınır.