jwtsecurityauthenticationvulnerabilitiestokensauth

JWT Vulnerabilities: The alg:none, Algorithm Confusion & Weak-Secret Attacks

·10 min read·Security & Auth

A JWT is not secure because it's a JWT

A JSON Web Token is just three Base64url-encoded JSON parts — header, payload, signature — joined by dots. Nothing about that format is secret or tamper-proof on its own; the *signature* is what makes a token trustworthy, and only if the server verifies it correctly. Almost every JWT breach comes down to a server that trusted a token it should have rejected. In 2026, several CVEs affecting cloud and enterprise systems still traced back to the same handful of validation mistakes below.

You can inspect any token's header and payload without the secret using a JWT decoder — which is the first hint that the payload is *not* confidential. Let's walk the classic attacks and exactly how to shut each one down.

Attack 1: the `alg: none` bypass

The JWT spec includes a "none" algorithm meaning *unsigned*. A naive library, told to accept whatever algorithm the token declares, will happily verify a token whose header says {"alg":"none"} and whose signature is empty — because "no signature" trivially matches "no algorithm."

An attacker takes a valid token, rewrites the header to alg: none, edits the payload to "role": "admin", drops the signature, and sends it:

json
{ "alg": "none", "typ": "JWT" }
{ "sub": "attacker", "role": "admin" }

If your server accepts it, they're an admin. The fix: never let the token choose the algorithm. Configure your library with an explicit allowlist — algorithms: ["RS256"] — and reject everything else, none included.

Attack 2: RS256 → HS256 algorithm confusion

This is the subtle one. Asymmetric algorithms like RS256 sign with a *private* key and verify with a *public* key — and the public key is, by design, public. Symmetric HS256 uses one *shared secret* for both signing and verifying.

The attack: the server expects RS256 and holds the RSA public key to verify. An attacker crafts a token with the header changed to HS256, then signs it using the server's public key as the HMAC secret. If the server's verification code picks the algorithm from the token header, it will try to verify an HS256 token — and helpfully use the public key (which the attacker also has) as the HMAC key. The signature matches. Forged token accepted.

text
Server's real setup:   verify( token, RSA_PUBLIC_KEY )   expecting RS256
Attacker's forged JWT: HS256, signed with RSA_PUBLIC_KEY as the secret
Vulnerable server:     "header says HS256, I'll HMAC-verify with my key" ✅ forged

The fix is the same principle as attack 1: pin the algorithm on the server. If you issue RS256, verify with algorithms: ["RS256"] only. Never accept multiple algorithm families on the same endpoint, and key your verification to the algorithm *you* chose, not the one the token advertises.

Attack 3: weak or leaked HMAC secrets

If you use HS256, the entire security of every token rests on one shared secret. A short, guessable, or committed-to-Git secret can be brute-forced offline: an attacker takes any valid token and tries candidate secrets until the signature recomputes. Dictionary attacks against weak JWT secrets are fast and well-tooled.

The fix: use a long, high-entropy secret (32+ random bytes, not a passphrase), keep it out of source control, and rotate it. For anything multi-service, prefer asymmetric RS256/ES256 so verifiers only ever hold the public key — see JWKS and key sets for distributing rotating public keys.

Attack 4: skipping the claims

Even a correctly-signed token is only trustworthy for what it actually asserts. Servers that check the signature but ignore the claims accept tokens that are expired, meant for another service, or issued by the wrong party. Always validate:

  • `exp` — reject expired tokens (and nbf if present).
  • `iss` — the issuer is one you trust.
  • `aud` — the audience is *this* API, so a token minted for another service can't be replayed against yours.

The JWT validation checklist

Every one of the attacks above is stopped by the same disciplined verification:

  1. Pin the algorithm to an explicit allowlist; never trust the token's alg, never allow none.
  2. Use asymmetric keys (RS256/ES256) for anything spanning services, so verifiers hold only public keys.
  3. Use a strong, rotated secret if you must use HS256.
  4. Validate `exp`, `iss`, and `aud` on every request, not just the signature.
  5. Keep secrets out of the payload — the payload is readable by anyone; confirm it with the JWT decoder.
  6. Keep tokens short-lived and pair them with refresh tokens; see where to store JWTs.

For the fundamentals see JWT tokens explained and safe JSON handling. To experiment, build test tokens with the JWT builder and inspect them with the decoder.

Frequently asked questions

No. A standard JWT (a JWS) is *signed*, not encrypted — the header and payload are only Base64url-encoded and anyone can read them. Never put secrets like passwords or API keys in a JWT payload. If you need confidentiality, use JWE.

An attacker changes a token's header algorithm to none (meaning unsigned), edits the payload, and removes the signature. Servers that accept whatever algorithm the token declares will treat the tampered token as valid. Pinning an algorithm allowlist that excludes none prevents it.

It's when a server that issues RS256 tokens can be tricked into verifying an HS256 token signed with its own public key as the HMAC secret. Because the public key is known to attackers, the forged signature verifies. The fix is to verify only with the exact algorithm you issued and never accept multiple algorithm families.

For HS256, use at least 32 bytes of cryptographically random data, kept out of source control and rotated periodically. Short or human-chosen secrets can be brute-forced offline from any valid token.

Yes. A valid signature only proves the token wasn't altered. You must still verify exp (not expired), iss (trusted issuer), and aud (intended for your API) to prevent replay of expired or misdirected tokens.

Try JSONKit — JSON Developer Tools

Format, validate, convert and diff JSON. Instant, accurate and 100% private.