← All cybersecurity posts

JWT Security: Common Vulnerabilities and Best Practices

JSON Web Tokens are everywhere: API authentication, single sign-on, mobile apps and service-to-service calls. They are also easy to get wrong, and a JWT mistake often means anyone can log in as anyone.

How a JWT works

A JWT has three base64url-encoded parts separated by dots: a header, a payload and a signature.

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoidXNlciJ9.signature

The payload is readable by anyone. The signature only proves the token was not modified, and only if the server verifies it correctly.

Vulnerability 1: Not verifying the signature

Some code decodes the token and trusts the payload without verifying. In many libraries, decode() does not verify, while verify() does. An attacker edits "role":"user" to "role":"admin" and is now an admin.

Vulnerability 2: The none algorithm

The JWT standard defines alg: none for unsigned tokens. Older or misconfigured libraries accept tokens with alg: none and no signature. Fix: explicitly specify the allowed algorithms when verifying.

jwt.verify(token, key, { algorithms: ["RS256"] });

Vulnerability 3: Algorithm confusion

If a server expects RS256, which uses a public and private key pair, but the library also accepts HS256, which uses a shared secret, an attacker can sign a token with HS256 using the public key as the secret. The server then verifies it with the same public key and accepts it. Fix: pin the algorithm.

Vulnerability 4: Weak HMAC secrets

Tokens signed with HS256 and a weak secret like secret or changeme can be cracked offline from a single captured token. Fix: use a long random secret, at least 256 bits, stored in a secrets manager, or use asymmetric keys.

Vulnerability 5: Header injection via kid, jku and x5u

These header fields tell the server which key to use. If the server trusts them without restriction, attackers can point to their own key, or use kid for path traversal or SQL injection in the key lookup. Fix: ignore jku and x5u unless you allowlist URLs, and treat kid as an untrusted lookup key.

Vulnerability 6: Missing claim validation

Even a valid signature is not enough. Validate:

  • exp, the expiry time.
  • nbf, not before.
  • iss, the issuer.
  • aud, the audience, so a token for one service cannot be replayed against another.

Vulnerability 7: Sensitive data in the payload

Because payloads are readable, never put secrets, passwords or sensitive personal data in a JWT.

Best practices summary

PracticeWhy
Always verify, never just decodePrevents forged tokens
Pin allowed algorithmsBlocks none and confusion attacks
Strong secrets or asymmetric keysPrevents offline cracking
Validate exp, iss, audPrevents replay and misuse
Short expiry plus refresh tokensLimits stolen token impact
Plan for revocationJWTs are hard to revoke otherwise

Testing JWT handling

SecurityInfinity agents capture tokens during authenticated testing and check for unsigned acceptance, algorithm confusion, weak secrets, header injection and missing claim checks. Start free.

Frequently asked questions

Are JWTs encrypted?

Standard signed JWTs are not encrypted. Anyone can read the payload. JWE is a separate standard for encrypted tokens.

Where should JWTs be stored in the browser?

HttpOnly cookies are safer against XSS than localStorage. If you store tokens in JavaScript-accessible storage, keep them short-lived.

How do you revoke a JWT?

Use short expiry, maintain a denylist of revoked token IDs, or check a per-user session version on each request.

Find what attackers can see, before they do.

AI pentesting agents test your web app, API and cloud, and every finding comes with evidence and a fix. First report in about 10 minutes.

Stay ahead of attackers.

New vulnerabilities, breach write-ups and practical fixes. A few emails a month.