What Are JWT Security Best Practices?
Learn JWT security best practices: strict signature verification, short-lived tokens, safe storage, and revocation.
Expected Interview Answer
Secure JWT usage means always validating the signature with a fixed, expected algorithm on the server, keeping tokens short-lived with refresh rotation, storing them somewhere JavaScript cannot read like an HttpOnly cookie, and never trusting claims without full verification.
A JWT’s payload is only base64-encoded, not encrypted, so anyone can read its claims — the security guarantee comes entirely from signature verification, and skipping that check or accepting the token’s own 'alg' header uncritically opens the door to the classic alg=none or RS256-to-HS256 confusion attacks where an attacker forges a token the server ends up trusting. Best practice is to hardcode the expected algorithm and key on the verification side rather than trusting whatever the token claims. Because a JWT cannot be invalidated the way a server-side session can, access tokens should be short-lived (minutes, not days), paired with a longer-lived refresh token that is stored in an HttpOnly, Secure, SameSite cookie so it is inaccessible to XSS and only sent to the intended origin. Sensitive data should never go in the payload since it is trivially decodable, and a compromised or logged-out token should be checked against a revocation mechanism (a short-TTL denylist or a token version/jti tracked server-side) since JWTs otherwise remain valid until they naturally expire.
- Rejects forged tokens via strict algorithm and signature verification
- Limits the blast radius of a leaked token through short expiry
- HttpOnly storage prevents XSS from reading and exfiltrating the token
- Refresh-token rotation and revocation lists provide a logout/kill-switch story
AI Mentor Explanation
A JWT is like a laminated player accreditation card anyone can read at a glance — name, team, access level — but that only counts as valid if the security office’s own hologram checker confirms the seal, never trusting a claim printed on the card itself about which checker to use. Officials keep accreditations valid for a single day rather than a season, so a lost card only exposes one day’s access. If a card is reported stolen, the gate immediately checks it against a same-day blocked list rather than waiting for natural expiry. That verify-independently, expire-fast, revoke-fast discipline is exactly how secure JWT handling works.
Step-by-Step Explanation
Step 1
Sign with a fixed algorithm
The server hardcodes the expected signing algorithm and key on verification, never trusting the token's own alg header.
Step 2
Issue short-lived access tokens
Access JWTs expire in minutes, limiting exposure from a leaked token.
Step 3
Store refresh tokens safely
A longer-lived refresh token lives in an HttpOnly, Secure, SameSite cookie, inaccessible to JS/XSS.
Step 4
Support revocation
A denylist or token-version check lets the server invalidate tokens before natural expiry, e.g. on logout.
What Interviewer Expects
- Understanding that JWT payloads are readable, not encrypted — security comes from signature verification
- Knowledge of alg=none and algorithm-confusion attacks and how to prevent them
- A concrete strategy for short-lived tokens plus refresh rotation and revocation
- Awareness of safe storage (HttpOnly cookie) versus XSS-exposed storage (localStorage)
Common Mistakes
- Trusting the alg field from the token itself during verification
- Storing JWTs in localStorage, exposing them directly to XSS
- Putting sensitive data in the payload, assuming it is encrypted
- Issuing long-lived access tokens with no revocation mechanism at all
Best Answer (HR Friendly)
“A JWT is basically a signed, tamper-evident token, but anyone can read what’s inside it, so you should never put secrets in there. The real security comes from checking that signature strictly on the server, keeping the tokens short-lived, storing them somewhere JavaScript can’t reach so a malicious script can’t steal them, and having a way to revoke them if something goes wrong, since you can’t just delete a JWT the way you’d delete a session.”
Code Example
const jwt = require('jsonwebtoken')
function verifyAccessToken(token) {
// Hardcode the algorithm — never trust the token’s own header
return jwt.verify(token, process.env.JWT_PUBLIC_KEY, {
algorithms: ['RS256'],
issuer: 'https://api.example.com',
maxAge: '15m',
})
}
app.get('/me', (req, res) => {
try {
const claims = verifyAccessToken(req.headers.authorization?.split(' ')[1])
res.json({ userId: claims.sub })
} catch (err) {
res.status(401).json({ error: 'Invalid or expired token' })
}
})Follow-up Questions
- How would you implement JWT revocation before natural expiry?
- What is the algorithm-confusion attack between RS256 and HS256?
- Why should JWT payloads never contain sensitive data?
- How do refresh token rotation and reuse detection work together?
MCQ Practice
1. Why is putting sensitive data directly in a JWT payload unsafe?
JWT payloads are base64-encoded, not encrypted, so their contents are readable by anyone who has the token.
2. What should the server do about the algorithm used to verify a JWT?
Trusting the token-supplied algorithm enables alg=none and algorithm-confusion forgery attacks.
3. Why can a JWT not simply be 'deleted' like a server-side session on logout?
Since JWTs are self-contained and stateless, a separate revocation mechanism is needed for early invalidation.
Flash Cards
Is a JWT payload encrypted? — No — it is only base64-encoded and readable by anyone with the token.
Why hardcode the verification algorithm? — To prevent alg=none and algorithm-confusion forgery attacks.
Where should refresh tokens be stored? — In an HttpOnly, Secure, SameSite cookie, inaccessible to JavaScript.
How do you revoke a JWT early? — A denylist or token-version/jti check on the server, since JWTs are otherwise stateless.