Understanding JWT: Structure, Security, and Common Pitfalls
How JSON Web Tokens are structured, where their security comes from, and the mistakes that leave authentication systems vulnerable.
By DevToolsBox · January 25, 2026
JSON Web Tokens (JWTs) are the workhorse of modern stateless authentication. They let a server hand a client a self-contained token that proves who the user is, without looking up a session on every request. But JWTs are also the source of many security mistakes — almost always because developers misunderstand what a JWT actually is. This article breaks down the structure, the security model, and the pitfalls to avoid.
The three parts of a JWT
A JWT is a string with three Base64URL-encoded segments separated by dots:
header.payload.signature
- Header — describes the token type and the signing algorithm, e.g.
{"alg":"HS256","typ":"JWT"}. - Payload — contains the claims: statements about the subject. Standard claims include
sub(subject/user ID),iat(issued-at),exp(expiration),iss(issuer), andaud(audience). You can also add custom claims like roles or scopes. - Signature — produced by signing the encoded header and payload with a secret (for HMAC) or a private key (for RSA/ECDSA). The signature lets the server verify the token has not been tampered with.
You can decode the header and payload yourself with the JWT Decoder — it simply reverses the Base64URL encoding and pretty-prints the JSON.
The single most important fact about JWTs
A JWT is encoded, not encrypted. Anyone who obtains the token can read every claim in the payload. The Base64URL encoding is just a transport format; it provides no confidentiality. The signature protects integrity (the token cannot be changed without invalidating the signature), not secrecy.
This has immediate consequences:
- Never put secrets in the payload. Passwords, API keys, and personal data must not travel in a JWT unless the token is also encrypted (JWE).
- Always transport tokens over HTTPS. Without TLS, an attacker on the network can read and steal the token.
- Treat access tokens as bearer credentials. Whoever holds the token can act as the user until it expires.
Where the security comes from
The security of a JWT rests entirely on the signature and the secret or key behind it. When the server receives a token, it re-computes the signature over the header and payload and compares it to the one in the token. If they match, the payload is trusted.
Common signing algorithms:
- HS256 (HMAC with SHA-256) — symmetric. The same secret signs and verifies. Simple, but the secret must be shared with every service that verifies tokens.
- RS256 (RSA signature with SHA-256) — asymmetric. A private key signs; a public key verifies. Ideal when many services need to verify tokens without being able to mint them.
- ES256 (ECDSA) — asymmetric, like RS256, but with smaller keys and signatures.
The signing algorithm is declared in the header — which is itself part of the token and therefore attacker-controllable. This leads to the most famous JWT vulnerability.
The alg: none attack
Early JWT libraries trusted the alg field in the header. An attacker could take a valid token, change the header to {"alg":"none"}, modify the payload (say, elevate their role to admin), drop the signature, and submit it. A vulnerable library would see alg: none, skip verification, and trust the payload.
The fix is straightforward: always verify the signature, and pin the expected algorithm on the server. Never accept alg: none, and never let the token dictate which algorithm the verifier uses. Modern libraries make this the default, but it is worth confirming.
Common pitfalls
- Storing JWTs in localStorage. This exposes them to any JavaScript running on the page, including cross-site scripting (XSS) payloads. Prefer an
HttpOnly,Secure,SameSitecookie for browser-based apps. - Long-lived access tokens. If a token is stolen, it is valid until it expires. Keep access tokens short-lived (minutes, not hours) and use a refresh token for obtaining new ones.
- No revocation. Stateless JWTs cannot be individually revoked without a server-side denylist. If you need logout-everywhere or immediate revocation, you need a stateful layer.
- Ignoring
exp. Always check theexp(andnbf) claims on the server. A token without an expiration is a liability. - Mistaking the signature for encryption. Remember: the payload is readable by anyone. If you decode a token with the JWT Decoder, you will see every claim in cleartext.
A safe mental model
Treat a JWT as a signed, readable assertion that the server issued. The signature proves authenticity; the exp claim bounds its lifetime; HTTPS protects it in transit; and a secure storage mechanism protects it at rest in the browser. Get those four things right and you will have avoided the vast majority of JWT security issues.
For day-to-day debugging, keep a JWT Decoder handy — but remember that it only decodes. Signature verification must always happen on your server, with the secret or public key you control.
Related Tools
JWT Decoder
Decode JWT tokens and inspect the header, payload, and signature. No data is sent to any server.
Base64 Encoder / Decoder
Encode text to Base64 or decode Base64 strings back to plain text. Supports UTF-8 and binary data.
Hash Generator
Generate MD5, SHA-1, SHA-256, SHA-384, and SHA-512 hashes from any text input.
JSON Formatter
Format, validate, and beautify JSON data instantly. Parse and fix malformed JSON with syntax highlighting.