$200 PERK Deploy cloud databases, droplets & cron jobs with $200 in free 60-day credits on DigitalOcean.
All Articles
Application Security Human-Authored & Plagiarism-Free DreaInno Research

Deconstructing JSON Web Tokens: RFC 7519 Claims, 'alg: none', and In-Browser Verification

JWTs are signed, not encrypted. We walk through Base64URL mechanics, the lifecycle of registered claims (iss, sub, exp), algorithm confusion vulnerabilities (RS256 vs HS256), and verifying signatures with the native Web Crypto API.

DP
Mayuresh Pandit • DreaInno Studio ↗
Principal Systems Architect
8 min read •
COMPANION BROWSER UTILITY

Execute the exact architecture explained in this article directly inside your browser sandbox. Zero data transmission.

Launch the Client-Side JWT Inspector
JSON Web Token RFC 7519 Structure and Web Crypto HMAC-SHA256 Signature Verification
JSON Web Token RFC 7519 Structure and Web Crypto HMAC-SHA256 Signature Verification ● ARCHITECTURAL SPECIFICATION

In modern microservice architectures, JSON Web Tokens (RFC 7519) are the de facto standard for stateless authorization. Yet misconceptions regarding how JWTs function continue to be a primary source of critical vulnerabilities in SaaS applications.

The Fundamental Misconception: Signed vs. Encrypted

The most pervasive security misconception among junior and mid-level engineers is that JSON Web Tokens protect the confidentiality of payload data.

Standard JWTs (JWS) are signed, NOT encrypted. Anyone who intercepts or possesses a JWT string can read every single field in the payload without knowing your secret key. The token is merely Base64URL encoded so it can be safely passed in HTTP headers (Authorization: Bearer <token>).

If you place sensitive PII, credit card numbers, or internal cryptographic seeds inside a standard JWT payload, you have published that information in plain text.

Anatomy of a Three-Part Token

A JWT consists of three segments separated by periods (.):

  1. Header: A JSON object specifying the token type and the cryptographic algorithm (e.g. {"alg": "HS256", "typ": "JWT"}).
  2. Payload: The body containing claims — statements about the entity (typically the user) and contextual metadata.
  3. Signature: A cryptographic hash calculated by signing Base64URL(Header) + "." + Base64URL(Payload) using your private key or shared secret.

Two Devastating JWT Vulnerabilities You Must Guard Against

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

The RFC specification allows an unsecured token where the algorithm is set to "none". In vulnerable libraries, if a malicious actor modifies the header to {"alg": "none"}, escalates their payload role from "user" to "admin", and strips the signature segment completely, the backend validation function returns valid = true!

Remediation: Modern verification middleware must explicitly whitelist allowed algorithms (e.g., exclusively ["RS256"] or ["HS256"]) and reject any token containing "none".

2. The Key Confusion Attack (RS256 vs. HS256)

In an asymmetric scheme (RS256), the authentication server signs tokens using a private RSA key, while consumer APIs verify signatures using a public RSA key published on a JWKS endpoint (/.well-known/jwks.json).

In a key confusion attack, an attacker fetches the public key (which is publicly readable by design), alters their token payload, and re-signs the token using the symmetric HS256 algorithm — using the public RSA key text as the secret HMAC key! If the backend verification function does not enforce the algorithm parameter, it will verify the HS256 signature using the public key string and accept the forged token.

In-Browser Cryptographic Verification with Web Crypto

You don't need external cloud servers to test and verify HMAC signatures. Modern browsers provide the window.crypto.subtle API, which allows hardware-accelerated signature verification in pure client-side TypeScript:

async function verifyHmac(token: string, secret: string): Promise<boolean> {
  const [headerB64, payloadB64, sigB64] = token.split('.');
  const data = new TextEncoder().encode(`${headerB64}.${payloadB64}`);
  
  // Import secret into browser crypto keystore
  const key = await window.crypto.subtle.importKey(
    'raw',
    new TextEncoder().encode(secret),
    { name: 'HMAC', hash: 'SHA-256' },
    false,
    ['verify']
  );

  // Decode Base64URL signature into bytes
  const sigBytes = Uint8Array.from(atob(sigB64.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0));
  
  // Verify purely in local memory
  return await window.crypto.subtle.verify('HMAC', key, sigBytes, data);
}

This code executes entirely within your browser's private memory space. Your secrets never leave your device.

ADVERTISEMENT ClientDev Tools
DEV RESOURCES Featured developer hardware & cloud partners:
Universal Vinyl Laptop Skin Decal (12.1" – 15.6") AMAZON (scratch-resistant vinyl protection) · DigitalOcean Droplets $200 FREE (60-day credit for cloud droplets & databases) · Hostinger KVM VPS 20% OFF (high-speed NVMe VPS with root SSH access)

Frequently Asked Engineering Questions

Should I store JWT session tokens in browser localStorage or cookies? ▼
For sensitive SaaS applications, store tokens in HttpOnly, Secure, SameSite=Strict cookies. localStorage is vulnerable to extraction via Cross-Site Scripting (XSS) attacks.
What is the purpose of the "nbf" (Not Before) claim? ▼
The nbf claim identifies the time before which the JWT must not be accepted for processing, preventing token use prior to scheduled activation.
DP
Mayuresh Pandit • Principal Systems Architect VERIFIED AUTHOR

Senior software architect at DreaInno Studio. Focuses on zero-trust browser runtimes, client-side AST tokenizers, distributed backend systems, and automated generative development agents.