Skip to content
Cipherly
Back to Blog

JWT Security Best Practices: Avoiding Common Mistakes

Cipherly TeamMay 2026Security

What is JWT?

JWT (JSON Web Token) is a compact, URL-safe means of representing claims to be transferred between two parties. Think of it as a digital passport—it contains information about who you are (claims) and proof that it's legitimate (signature).

JWTs are widely used for authentication and authorization in modern web applications. A typical JWT looks like this:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWT Structure

A JWT consists of three parts separated by dots (.), each base64-encoded:

  1. Header: Specifies the type (JWT) and the signing algorithm (HS256, RS256, etc.)
  2. Payload: Contains claims (user ID, permissions, expiration time, etc.)
  3. Signature: Ensures the token hasn't been tampered with

Common JWT Vulnerabilities

1. Using "none" Algorithm

Some JWT libraries support an algorithm called "none", which skips signature verification. This is extremely dangerous and should never be allowed in production.

{alg: "none"} ← NEVER use this

2. Weak Secrets

If you're using HMAC (HS256), your secret must be strong. A weak secret can be brute-forced.

3. Not Validating Expiration (exp)

Always check if a token has expired. An old stolen token should not grant access.

4. Algorithm Substitution

An attacker might change RS256 (asymmetric) to HS256 (symmetric) and use your public key as the HMAC secret.

5. Storing Sensitive Data in Payload

Remember: JWT payload is base64-encoded, not encrypted. Anyone can decode it. Don't store passwords, API keys, or SSNs.

JWT Best Practices

✅ Security Checklist

Use Strong Signing Algorithms

Use RS256, HS512, or newer. Never use "none" or weak algorithms like MD5.

Use a Strong Secret (for HMAC)

At least 256 bits of random data. Example: 64 random hex characters.

Always Validate Expiration

Check the "exp" claim on every request. Expired tokens should be rejected.

Set Short Expiration Times

15-30 minutes is common. This limits the damage if a token is stolen.

Use HTTPS

Always transmit JWTs over HTTPS. Never use HTTP.

Store Tokens Securely

In the browser, use httpOnly cookies rather than localStorage (protects against XSS).

Include Issued-At (iat) Claim

This helps detect tokens that were issued before a known compromise.

Use Subject (sub) Claim

Identify who the token belongs to (user ID, email, etc.).

Implement Token Refresh

Use short-lived access tokens and longer-lived refresh tokens.

Validate Algorithm Explicitly

Don't accept any algorithm. Specify exactly which algorithm(s) you expect.

JWT Payload Example (Secure)

{

"sub": "user123",

"email": "user@example.com",

"role": "admin",

"iat": 1516239022,

"exp": 1516242622, // 1 hour

"nbf": 1516239022

}

✅ Includes expiration, issued-at, subject, and no sensitive data.

When NOT to Use JWT

  • When you need to revoke tokens immediately (JWTs can't be revoked until expiration)
  • When storing highly sensitive data (use server-side sessions instead)
  • For very short-lived tokens where a session would be more efficient

Validate Your JWTs

Use Cipherly's JWT Debugger to decode, inspect, and validate your tokens. Check signatures, verify claims, and ensure your tokens are properly formatted.

Try JWT Debugger