Home / Blog / JWT Security: Decode Tokens Safely and Dodge the Classic Attacks

JWT Security: Decode Tokens Safely and Dodge the Classic Attacks

8 min read · Updated 2026-08-07

JSON Web Tokens look intimidating — three base64 blobs glued with dots — but they're among the simplest auth artifacts to inspect. Understanding what's inside yours is the first line of defense.

Anatomy in 30 seconds

A JWT is header.payload.signature. The header declares the algorithm ({"alg":"HS256","typ":"JWT"}). The payload carries claims: sub (user), iat/exp (issued/expires), plus custom data. The signature is HMAC or RSA over the first two parts. Crucial fact: header and payload are base64url-encoded, not encrypted. Anyone can read them — never put secrets in a JWT payload. Paste a token into the JWT decoder to see this for yourself; it's fully client-side, so your token never leaves the page.

Attack #1: alg=none

The JOSE spec reserved "alg":"none" for unsecured tokens. Some libraries accept it even in production — an attacker rewrites the header to alg:none, strips the signature, and becomes any user. Fix: server must whitelist the exact expected algorithm and reject everything else.

Attack #2: algorithm confusion (RS256→HS256)

Server uses RSA: verifies with a public key. If the verification code trusts the token's alg header, an attacker switches to HS256 and signs with the public key as the HMAC secret — which by definition is public. Fix: pin the algorithm server-side, not the header's say-so.

Attack #3: weak HMAC secrets

HS256 tokens are only as strong as the secret. "secret", "password123", the project name — cracking rigs grind through millions per second. Secrets should be 256+ bits of CSPRNG output, stored in a secrets manager, rotated on any suspicion.

Expiry and revocation gaps

Every production token needs a short exp — 15 minutes is a sane default — plus a refresh-token flow. Stateless JWTs can't be individually revoked; for logout-is-instant requirements, keep a blocklist (jti → TTL) or move to short-lived tokens with rotation. Also validate iss and aud — a token minted for service A shouldn't authenticate to service B.

The safe debugging workflow

When an auth bug hits: decode locally (offline tool, not a random website that might log tokens), check exp vs your clock first — clock skew is the silent killer ("token expired" at 4:59 when it expires at 5:00 because a container's clock drifted). Then verify claims, then algorithm config. Never paste production user tokens into third-party websites.

Sponsor — keeps these guides free

Frequently Asked Questions

Is decoding a JWT the same as verifying it?

No. Decoding just base64-decodes the readable parts — anyone can do it, which is why no secrets belong inside. Verifying requires the key and checks the signature's integrity.

Is it safe to paste a JWT into an online decoder?

Only if it's fully client-side (DevToolBox's is — zero network calls). A decoded token still grants whoever holds it whatever access remains until expiry, so treat tokens like passwords in transit.

What does 'invalid signature' mean?

The token's content was altered after signing, or you're verifying with the wrong key/algorithm. If you just clocked a 401, check exp first — it's usually expiry, not signature.

How long should JWT expiry be?

Common production pattern: access token 5–15 minutes, refresh token days to weeks with rotation. Balance UX (fewer logins) against token-theft exposure window.

💜 Found this guide useful?

☕ Buy Me a Coffee