Limited Offer

30% OFF Lifetime Access ($139) with code SYSTEM30

TOPIC #160Intermediate 10 min read

JWT Deep Dive: Structure, Signing, & Critical Pitfalls

💡
Core Architecture Summary

Master JSON Web Tokens: Base64URL anatomy, symmetric HS256 vs asymmetric RS256/ES256, JWKS key rotation, the "alg: none" vulnerability, RSA-to-HMAC key confusion attacks, and JWS vs JWE differences.

Key Glossary Concepts in this TopicAll Glossary Terms

JWT Structure & Asymmetric JWKS Verification 📜

Deconstructing the 3-part Base64URL token and asymmetric signature verification across microservices.

JWT Structure & Asymmetric JWKS Verification 📜
100%
Rendering visual architecture flowchart...

01.1. Anatomy of a JSON Web Token (JWS)

A standard JSON Web Signature (JWS) token consists of three Base64URL-encoded JSON segments separated by literal periods (.): header.payload.signature.

1. Header (Metadata):

Defines the cryptographic algorithm and key metadata:

json
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "auth-key-2026-v1"
}
  • alg: The cryptographic signing algorithm (e.g., RS256 = RSA Signature with SHA-256; ES256 = ECDSA with P-256 and SHA-256; HS256 = HMAC-SHA256).
  • typ: Token type, typically "JWT".
  • kid: Key Identifier used by the verifying service to select the correct public key from a JWKS set.

2. Payload (Claims):

Contains statements about the subject entity and contextual metadata:

  • Registered Claims (RFC 7519 standard):
    • iss (Issuer): Who created and signed the token (e.g., https://auth.company.com/).
    • sub (Subject): Unique principal ID (e.g., usr_981442).
    • aud (Audience): Who the token is intended for (e.g., https://api.company.com/billing).
    • exp (Expiration Time): Unix epoch timestamp after which the token must be rejected.
    • nbf (Not Before): Unix epoch timestamp before which the token is invalid.
    • iat (Issued At): Unix epoch timestamp of token generation.
    • jti (JWT ID): Unique identifier for the token (used to detect replay attacks).
  • Custom / Application Claims: Arbitrary business data (e.g., roles: ["admin", "billing_manager"], tenant_id: "acme_corp").

3. Signature:

Guarantees integrity and authenticity. For RS256:

Signature = Sign_{PrivateKey}(Base64URL(Header) + "." + Base64URL(Payload))

02.2. Symmetric (HS256) vs Asymmetric (RS256 / ES256) Signing

Choosing the right signing algorithm is the single most critical architectural decision when designing JWT systems:

Symmetric (HMAC-SHA256 / HS256):

  • Mechanism: Uses a single shared symmetric secret key (e.g., a 256-bit random hex string) for both signing and verifying.
  • The Microservice Danger: If 50 microservices need to verify user tokens, all 50 services must possess the shared secret. If a single low-security microservice is compromised, the attacker extracts the secret and can forge valid tokens for any user or administrator across the entire ecosystem.

Asymmetric (RS256 / ES256 / Ed25519):

  • Mechanism: Uses a public/private keypair.
    • Private Key: Held exclusively by the centralized Identity Provider (Auth0, Okta, internal Auth Service). Only the IdP can sign tokens.
    • Public Key: Published openly via a JSON Web Key Set (JWKS) endpoint (e.g., https://auth.example.com/.well-known/jwks.json).
  • Zero Leakage Risk: Downstream microservices only download the public key. Even if an attacker compromises a microservice, they cannot forge signatures.
  • Modern Recommendation: Prefer ES256 (ECDSA) or Ed25519 over RS256 for faster computation (< 0.1ms) and smaller key sizes (256 bits vs 2048/4096 bits for equivalent cryptographic strength).

03.3. Critical JWT Security Pitfalls & Exploits

Numerous high-severity CVEs in major JWT libraries stem from improper signature validation logic:

1. The "alg": "none" Vulnerability (CVE-2015-9235):

The JWT specification allows an algorithm header value of "none" for unsecure tokens. Attackers strip the signature, change "alg": "RS256" to "none", alter the payload to "role": "admin", and submit the token. Vulnerable libraries accept the forged payload without checking the signature.

  • Defense: Explicitly configure JWT verification libraries to whitelist allowed algorithms: algorithms: ['RS256', 'ES256'] and reject none.

2. The RSA-to-HMAC Key Confusion Attack (CVE-2016-5431):

If an application supports both RS256 and HS256, an attacker takes the server's public RSA key (which is publicly available on the web) and uses it as the HMAC secret to sign a forged token with "alg": "HS256". If the backend parser uses a generic verify function that accepts any algorithm indicated in the token header, it verifies the signature using the RSA public key string with HMAC, which matches and passes validation!

  • Defense: Never determine the verification algorithm from the incoming token's unverified alg header. Hardcode or strictly enforce the expected algorithm in the verifier.

3. JWS vs JWE (Signing vs Encryption):

  • JWS (Signed JWT): Encoded in plaintext Base64URL. Anyone who intercepts the token can read all payload fields. Never store raw passwords, social security numbers, or sensitive API secrets in JWT claims.
  • JWE (Encrypted JWT): Encrypts the payload so that only holders of the decryption key can read the claims. Used when claims contain sensitive personal data.

04.4. Production TypeScript Verification with JWKS Caching

Below is a robust Node.js implementation using jwks-rsa and jsonwebtoken that securely fetches and caches public keys while guarding against known vulnerabilities:

typescript
import jwt, { JwtHeader, SigningKeyCallback } from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

// Configure secure JWKS client with in-memory caching & rate-limiting
const client = jwksClient({
  jwksUri: 'https://auth.company.com/.well-known/jwks.json',
  cache: true,
  cacheMaxEntries: 10,
  cacheMaxAge: 60 * 60 * 1000, // 1 hour TTL
  rateLimit: true,
  jwksRequestsPerMinute: 10 // Prevent DoS on JWKS endpoint
});

function getKey(header: JwtHeader, callback: SigningKeyCallback): void {
  if (!header.kid) {
    return callback(new Error('Missing kid (Key ID) header in JWT'));
  }

  client.getSigningKey(header.kid, (err, key) => {
    if (err || !key) {
      return callback(err || new Error('Signing key not found in JWKS'));
    }
    const signingKey = key.getPublicKey();
    callback(null, signingKey);
  });
}

export interface ValidatedUserClaims {
  sub: string;
  role: string;
  tenantId: string;
}

export function verifyUserToken(token: string): Promise<ValidatedUserClaims> {
  return new Promise((resolve, reject) => {
    jwt.verify(
      token,
      getKey,
      {
        // DEFENSE: Strictly whitelist expected algorithms (prevents "none" and key confusion)
        algorithms: ['RS256'],
        issuer: 'https://auth.company.com/',
        audience: 'https://api.company.com/'
      },
      (err, decoded) => {
        if (err) {
          return reject(new Error(`Token verification failed: ${err.message}`));
        }
        resolve(decoded as ValidatedUserClaims);
      }
    );
  });
}

⚖️Architectural Trade-offs & Production Realities

Architectural Advantages

  • RS256/ES256 with JWKS enables seamless, decentralized verification across polyglot microservices without sharing secrets
  • Standardized claims (iss, sub, aud, exp, iat) simplify cross-organizational federation
  • Public key rotation via JWKS requires zero deployment downtime on downstream services

Trade-offs & Constraints

  • Payload claims are visible to anyone in plaintext; sensitive PII must not be stored in standard JWS tokens
  • Misconfigured verification libraries that dynamically trust the "alg" header are susceptible to critical exploits (alg: none, key confusion)
  • JWT tokens are bulky compared to 32-byte opaque tokens, increasing HTTP header overhead on every request
Production Implementation in Big Tech
GitHub Actions & AWS IAM• OIDC Workload Identity Federation

When a GitHub Actions workflow deploys to AWS, GitHub generates a short-lived (10-minute) RS256 JWT. AWS IAM verifies the token against GitHub's public JWKS endpoint (`https://token.actions.githubusercontent.com/.well-known/jwks.json`) and exchanges it for temporary AWS STS credentials, completely eliminating hardcoded AWS secret keys from GitHub repositories.

🎯 Staff+ Engineering Takeaways

  • JWT consists of Header, Payload, and Signature in Base64URL encoding.
  • Always use asymmetric algorithms (RS256 / ES256) for microservice ecosystems.
  • Strictly whitelist verification algorithms to eliminate "alg: none" and RSA-to-HMAC key confusion attacks.
  • Never store sensitive confidential secrets in standard JWS claims; they are readable by anyone.

Topic Knowledge Assessment 🧠

Step through 2 scenario questions to test your staff-level grasp.

Question 1 of 20 answered
#1

What is the root vulnerability underlying the RSA-to-HMAC "Key Confusion" attack in JWT validation libraries?

Rate This Architecture Chapter4.9 / 5.0 (38 ratings)

How clear and staff-actionable was this system breakdown?