EngineeringAI Assisted

Understanding JSON Web Tokens (JWT): Structure, Security Vulnerabilities, and Debugging Guide

Master JSON Web Tokens (JWT) in modern authentication. Learn the anatomy of Headers, Payloads, and Signatures (HMAC vs RSA/ECDSA), prevent security pitfalls like algorithm confusion, and debug tokens in real-time.

JJ
Joey Jazwinski
September 14, 20266 min read

In modern stateless web architectures, microservices, and Single-Page Applications (SPAs), JSON Web Tokens (JWTs) have become the industry standard for transmitting verifiable identity claims between parties. Whether authenticating API requests via OAuth 2.0 / OpenID Connect, managing session state across serverless functions, or passing user authorization scopes, JWTs are everywhere.

However, despite their ubiquity, JWTs are frequently misunderstood and misconfigured. Developers often confuse encoding with encryption, inadvertently leak sensitive data in payloads, or introduce critical authentication vulnerabilities such as the none algorithm exploit and key confusion attacks.

In this educational tutorial, you will learn the exact internal anatomy of a JWT (RFC 7519), understand symmetric vs. asymmetric cryptographic signatures, review defensive best practices, and debug your tokens using interactive browser-based developer tools.

πŸ’‘ Key Takeaways (TL;DR)#

  • Three-Part Structure: A JWT is a Base64URL-encoded string consisting of three parts separated by dots: Header.Payload.Signature.
  • Encoded, Not Encrypted: By default, standard JWTs are signed, not encrypted (JWS). Anyone with access to the token string can decode and read the JSON payload. Never store raw passwords, API keys, or sensitive PII in a payload.
  • Symmetric vs. Asymmetric Signing: HS256 uses a shared symmetric secret (HMAC-SHA256). RS256/ES256 uses a private key for signing and a public key for verification, making it ideal for microservice architectures.
  • Critical Security Gotchas: Always enforce allowed algorithms on your backend to prevent alg: "none" bypasses, set strict exp (expiration) timestamps, and store tokens in secure HttpOnly; SameSite=Strict cookies.
  • Interactive Debugging: Inspect token headers, claims, and signature validity directly in your browser using the JWT Debugger.

1. Anatomy of a JSON Web Token#

A JSON Web Token is a compact, URL-safe string containing three distinct segments separated by periods (.):

Code
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvZXkiLCJpYXQiOjE1MTYyMzkwMjJ9.4zU_...
└────────────── Header β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜.└────────────── Payload β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜.└───────── Signature β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Segment 1: The Header#

The header specifies the token type and the cryptographic algorithm used to generate the signature:

json
{
  "alg": "HS256",
  "typ": "JWT"
}

Segment 2: The Payload (Claims)#

The payload contains the claimsβ€”statements about an entity (typically the user) and additional metadata. Claims fall into three categories:

  1. Registered Claims (Predefined):
    • sub (Subject): Unique user ID (e.g. usr_90210).
    • iss (Issuer): The identity provider issuing the token (e.g. https://auth.joeyjazwinski.com).
    • exp (Expiration Time): Unix timestamp when the token becomes invalid.
    • iat (Issued At): Unix timestamp when the token was created.
  2. Public Claims: Standardized custom names defined in the IANA JSON Web Token Registry.
  3. Private Claims: Custom application-specific properties (e.g. role: "admin", tenantId: "org_42").
json
{
  "sub": "usr_90210",
  "name": "Joey Jazwinski",
  "role": "admin",
  "iat": 1773489600,
  "exp": 1773493200
}

Segment 3: The Signature#

The signature prevents tampering. It is computed by taking the Base64URL-encoded header, Base64URL-encoded payload, and hashing them with a secret key:

Code
Signature = HMACSHA256(
  base64UrlEncode(header) + "." + base64UrlEncode(payload),
  SECRET_KEY
)

2. Interactive Tool: Inspect & Verify Tokens in Real-Time#

πŸ› οΈ Live Developer Tool Callout#

Need to quickly decode a token payload, inspect standard claims, or verify HMAC/RSA signatures?


3. Cryptographic Signing: HS256 vs. RS256#

Understanding the difference between symmetric and asymmetric signing is crucial for secure distributed systems:

Symmetric vs. Asymmetric Comparison#

FeatureHS256 (HMAC-SHA256)RS256 / ES256 (RSA / ECDSA)
Key TypeSingle Shared SecretPrivate Key (Sign) + Public Key (Verify)
PerformanceExtremely Fast (Minimal CPU overhead)Slightly slower (Higher cryptographic computation)
Security RiskIf one microservice is compromised, attackers can forge tokensMicroservices only receive the public key; cannot forge tokens
Best Used ForMonolithic backends, internal private servicesDistributed microservices, OAuth2 Identity Providers (IdP)

4. Common JWT Security Vulnerabilities & How to Prevent Them#

1. The alg: "none" Algorithm Exploit#

In flawed JWT verification libraries, an attacker can modify the token header to {"alg": "none"}, strip the signature, and submit arbitrarily modified payload claims (e.g. elevating role: "user" to role: "admin"). If the backend blindly trusts the header algorithm, it validates the token without a signature!

Defense: Never trust the algorithm declared in the header. Explicitly specify expected algorithms in your verification options:

typescript
// Backend Verification in Node.js / TypeScript
import jwt from 'jsonwebtoken';

export function verifyUserToken(token: string, publicKey: string) {
  return jwt.verify(token, publicKey, {
    algorithms: ['RS256'], // Explicitly whitelist allowed algorithms!
  });
}

2. Key Confusion Attacks (HMAC vs. RSA)#

When a server supports both RS256 and HS256, an attacker can take the server's public RSA key (which is public knowledge) and use it as a symmetric secret to sign a token with HS256. If the verification function uses the same key variable without checking the algorithm, it validates the forged HMAC signature using the public key string.

Defense: Strictly separate verification pathways for asymmetric and symmetric tokens.


3. Insecure Token Storage (XSS vs. CSRF)#

Where you store your JWT in the browser dictates your vulnerability exposure:

  • Avoid localStorage: Any cross-site scripting (XSS) vulnerability in a third-party npm package can read localStorage.getItem('token') and exfiltrate your user sessions.
  • Use HttpOnly; Secure; SameSite=Strict Cookies: Browsers automatically attach the cookie to API requests, but JavaScript cannot read or extract it.

5. Token Revocation & Refresh Token Pattern#

Because JWTs are stateless, once issued, a token remains valid until its exp timestamp passes. If a user changes their password or reports a compromised account, how do you revoke access?

The standard enterprise pattern utilizes Short-Lived Access Tokens (10–15 minutes) paired with Long-Lived Refresh Tokens (7–30 days) stored in a database:


Frequently Asked Questions#

Can I encrypt a JWT if I need to store sensitive data?#

Yes. If payload confidentiality is required, use JSON Web Encryption (JWE) (RFC 7516), which encrypts payload contents so that only the holder of the decryption key can view the claims.

What is the ideal expiration time for an access token?#

For security-conscious applications, access tokens should expire within 5 to 15 minutes. Long-lived access tokens (hours or days) expand the blast radius if intercepted.

How do I handle clock skew between servers?#

Network servers may have slight clock drift. When verifying exp and iat claims, configure a small tolerance (e.g. clockTolerance: 30 seconds) in your JWT verification library to prevent false rejection of valid tokens.

JJ

Joey Jazwinski

Hi, I'm Joey β€” a software engineer building modern applications, exploring artificial intelligence, and sharing my journey through code. πŸš€

Comments