← Blog
// RED TEAM · WEB

JWT: the 3 flaws I see in every pentest

Published Aug 12, 2026 · 7 min read
Red TeamWebJWTAPI

JSON Web Tokens are everywhere: APIs, login, SSO. And they hide a mental trap: they look opaque, but a JWT is not encrypted, it is encoded. Anyone can read its contents without a key. That is not the flaw —it is by design— but it is the door the three implementation mistakes I keep running into walk through.

Anatomy in 20 seconds

A JWT is three Base64url parts separated by dots: header.payload.signature.

eyJhbGciOiJIUzI1NiJ9  .  eyJ1c2VyIjoiYWRtaW4ifQ  .  <signature>
   └─ {"alg":"HS256"}        └─ {"user":"admin"}         └─ HMAC/​RSA

The header and payload are readable by anyone (just Base64). The only thing stopping forgery is the signature. All of the token's security rests on the server verifying it correctly. Fail there and it is over.

🔓 To read a token's insides (alg, claims, expiry) without pasting it into third-party sites, use the JWT Decoder: header and payload in your browser, nothing sent anywhere. The first thing I always check: the alg field.

Flaw 1 — alg: none

The spec allows the none algorithm: an "unsigned" token. If the server accepts it, the attacker changes the header to {"alg":"none"}, drops the signature and rewrites the payload at will:

{"alg":"none"}.{"user":"admin","role":"admin"}.

With no signature check, the backend believes it. That is direct privilege escalation. The defense: never accept none, and do not let the token choose the algorithm.

Flaw 2 — weak HMAC secret (HS256)

With HS256 the signature is an HMAC using a shared secret. If that secret is weak (secret, changeme, a dictionary word), it breaks offline: capture a valid token and brute-force the signature (hashcat mode 16500, jwt_tool). Once the secret falls, you can sign any token: you are whoever you want to be.

⚠️ The JWT secret is a top-criticality credential, not just another env var. Treat it like a root key: long, random, rotated and out of the repo.

Flaw 3 — algorithm confusion (RS256 → HS256)

With RS256 the token is signed with the private key and verified with the public one (which is, by definition, public). The classic bug: the attacker switches the header to HS256 and signs the token using the public key as the HMAC secret. If the server does not pin the algorithm and just uses "the key", it verifies with the public key as a symmetric secret… and validates the forged token. Its dumber cousin is the simplest bug of all: not verifying the signature at all (more common than you'd think in hand-rolled middleware).

And a reminder: the payload is not a secret

Since it is readable, putting sensitive data in the payload (emails, internal roles, IDs, third-party tokens) leaks it to anyone who captures the JWT. The payload has integrity if the signature is good, but it is never confidential.

How to harden it (server side)

Triage checklist (offensive side)

JWT is not insecure; almost always it is how it is verified that is. And since the token reads without a key, the first step —looking inside— is available to anyone. Make sure you take that look before an attacker does.

Share: LinkedIn X
Sergio Belmonte Morales
Sergio Belmonte Morales
Cybersecurity Analyst · SOC · Sentinel/KQL specialist