Intermediate·18 min read·Updated 2026-10-15

JSON Web Token (JWT) Security: Vulnerabilities, Storage & Implementation Guide

Original WeSecureX guide for authorized testing of sites you own. Linked Sources are citations for verification - not copied articles. Ads are not served on this page.

JSON Web Tokens (JWT, RFC 7519) are the de facto standard for stateless authentication in modern microservices, single-page applications (SPAs), and mobile backends. However, their flexibility has made them a frequent source of severe security vulnerabilities. From developers inadvertently accepting the unsigned `"alg": "none"` algorithm to exposing signing secrets that can be cracked offline in seconds, improper JWT implementations frequently lead to full authentication bypass. Furthermore, storing JWTs in browser `localStorage` exposes session tokens to extraction via Cross-Site Scripting (XSS). This guide explores token anatomy, signature verification pitfalls, asymmetric cryptography (RS256 vs. HS256), and secure cookie-based token delivery. Verify your API headers and endpoint defenses with the [WeSecureX Tools Directory](/tools) and [Security Headers Checker](/tools/security-headers-checker).

Quick Answer

Secure JWTs by explicitly rejecting the 'none' algorithm and asymmetric/symmetric key confusion, signing tokens with strong 256-bit asymmetric keys (RS256 or EdDSA), enforcing short expiration times (5-15 minutes) with rotating refresh tokens, and storing tokens in HttpOnly, Secure, SameSite=Strict cookies.

Key Takeaways
  • JWT header alg: none vulnerabilities permit unauthenticated administrative impersonation.
  • Algorithm confusion attacks exploit servers that fail to restrict verification to asymmetric algorithms.
  • Storing JWTs in browser localStorage exposes them directly to script-based XSS extraction.
  • HttpOnly, Secure cookies combined with CSRF protection offer the most resilient token storage model.
Visual Diagram · JWT Token Security Flow & Signature Verification Pipeline
1

1. Token Ingress

Client presents JWT in Authorization: Bearer <token> or via an HttpOnly cookie.

2

2. Algorithm Whitelist

Server validates that header alg matches an explicitly permitted list (e.g., strictly RS256).

3

3. Cryptographic Signature

Public key validates cryptographic signature against Base64Url header and payload.

4

4. Claims & Expiration

Server checks exp, nbf, and iss claims to ensure the token is active and valid.

ShareShare on XLinkedIn

1. The Anatomy of a JWT & The 'alg: none' Exploit

WeSecureX Vulnerabilities Catalog detailing authentication bypass and token flaws
WeSecureX Vulnerabilities Catalog detailing authentication bypass and token flawsWeSecureX Platform View

A JSON Web Token consists of three distinct Base64URL-encoded segments separated by periods: Header.Payload.Signature. The header defines metadata about the token, including the signing algorithm ({"alg": "HS256", "typ": "JWT"}). The payload contains declared claims about the user or session ({"sub": "user_102", "role": "member", "exp": 1718000000}). The signature is generated by hashing the header and payload with a secret or private key.

In early JWT specifications, the RFC permitted an unsigned algorithm token designated as "alg": "none" for testing and debugging environments. Vulnerable JWT libraries respected this header without verifying application configuration. An attacker can intercept any valid token, change "alg": "HS256" to "alg": "none", alter the payload claims to {"role": "superadmin"}, strip the signature entirely, and submit the modified token. If the server does not enforce a strict algorithm allowlist, it accepts the unverified token as valid, granting the attacker complete administrative control.

Modern authentication architectures must explicitly disallow none and reject any token lacking a valid cryptographic signature. Learn more about secure API architecture in our Cybersecurity Best Practices Guide and verify your endpoints using the WeSecureX Vulnerabilities Catalog.

typescript
// Forged alg: none JWT Token Example
// Header: {"alg":"none","typ":"JWT"} -> eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0
// Payload: {"sub":"admin","role":"superuser"} -> eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJzdXBlcnVzZXIifQ
// Signature: (empty)

const forgedToken = "eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJzdXBlcnVzZXIifQ.";

// SECURE VERIFICATION IN NODE.JS (jsonwebtoken)
import jwt from 'jsonwebtoken';

export function verifyUserToken(token: string, publicKey: string) {
  // CRITICAL: Explicitly specify permitted algorithms!
  return jwt.verify(token, publicKey, {
    algorithms: ['RS256'], // Rejects 'none' and 'HS256' automatically
    issuer: 'https://auth.example.com',
    audience: 'https://api.example.com'
  });
}

2. Algorithm Confusion Attacks: RSA vs. HMAC (CVE-2015-9235)

WeSecureX Security Headers Checker validating JWT cookie flags and transport security
WeSecureX Security Headers Checker validating JWT cookie flags and transport securityWeSecureX Platform View

One of the most insidious architectural flaws in JWT authentication is the Algorithm Confusion (or Key Confusion) Attack. Many organizations adopt asymmetric cryptography (RS256), where the authentication server signs tokens using a private RSA key, and decentralized microservices verify tokens using a publicly distributed public key (frequently published at /.well-known/jwks.json).

In a key confusion attack, the adversary downloads your public RSA key. They construct a forged JWT payload declaring "role": "admin", but modify the header from "alg": "RS256" to symmetric "alg": "HS256" (HMAC-SHA256). The attacker then signs the token using your public RSA key as the HMAC shared secret. If the backend verification library passes the public key string into a generic jwt.verify(token, key) function without pinning the algorithm, the library treats the public key as an HMAC secret string. Because the attacker signed the token using that exact string, the signature verification check passes with 100% mathematical validity!

To neutralize key confusion attacks, verification logic must never allow the token header to dictate which cryptographic verification method is invoked. Always enforce an explicit algorithm allowlist pinning verification strictly to RS256 or ES256. Verify your security headers and transport security with the Security Headers Checker.

Algorithm TypeSigning KeyVerification KeySecurity Recommendation
RS256 / ES256Private Key (Kept strictly on auth server)Public Key (Distributed to verification microservices)Recommended: Immune to secret leakage across distributed services
HS256Symmetric Shared Secret (256-bit min)Identical Shared SecretAcceptable only in monolithic apps; vulnerable if secret is brute-forced
noneNoneNoneProhibited: Completely disables authentication integrity

3. Token Storage: HttpOnly Cookies vs. LocalStorage

WeSecureX Website Scanner testing authentication cookies and token endpoints
WeSecureX Website Scanner testing authentication cookies and token endpointsWeSecureX Platform View

Where you store authentication tokens on the client defines your vulnerability profile against web attacks. Single Page Applications (SPAs) frequently store JWTs in browser localStorage or sessionStorage. While this makes tokens easy to access via JavaScript for API calls, it exposes your application to catastrophic credential theft: any Cross-Site Scripting (XSS) vulnerability anywhere on the domain allows an attacker to execute localStorage.getItem('token') and exfiltrate credentials instantly.

To protect session credentials, store access and refresh tokens in HttpOnly, Secure, SameSite=Strict cookies. Because HttpOnly cookies are completely inaccessible to client-side JavaScript APIs, an XSS attacker cannot extract or clone the token. Combine this with short access token lifetimes (5 to 15 minutes) and rotating refresh tokens stored in secure database tables. When an access token expires, the client calls /api/auth/refresh, which validates the refresh token, invalidates the old token, and issues a new pair.

Audit your web application's susceptibility to script injection and token theft using the WeSecureX XSS Scanner and run complete perimeter scans with the Website Security Scanner.

typescript
// Secure Cookie-Based JWT Issuance in Next.js / Express
export function setAuthCookies(res: Response, accessToken: string, refreshToken: string) {
  // Access Token: Short-lived (15 minutes)
  res.cookie('access_token', accessToken, {
    httpOnly: true,     // Blocks JavaScript access (XSS immune)
    secure: true,       // Enforces encrypted HTTPS transmission
    sameSite: 'strict', // Blocks cross-origin transmission (CSRF defense)
    path: '/api',
    maxAge: 15 * 60 * 1000 // 15 minutes
  });

  // Refresh Token: Bound strictly to the token refresh endpoint
  res.cookie('refresh_token', refreshToken, {
    httpOnly: true,
    secure: true,
    sameSite: 'strict',
    path: '/api/auth/refresh',
    maxAge: 7 * 24 * 60 * 60 * 1000 // 7 days
  });
}

Apply it with WeSecureX

Add a hostname you own or are allowed to test, run a scan, and open the findings for that target. A confirmed result includes the request WeSecureX sent and the response the site returned, so you can reproduce it, fix it in your own application, and rescan the same host to confirm the issue is gone. WeSecureX is automated testing, not a manual penetration test, and a clean report is not a promise that every weakness is gone.

Checks that belong with this topic

  • Explicitly enforce accepted algorithms in verification options (e.g., algorithms: ['RS256']).
  • Reject tokens with alg: none or invalid cryptographic signatures.
  • Store sensitive authentication tokens in HttpOnly, Secure, SameSite=Strict cookies.
  • Set short access token lifespans (5 to 15 minutes) and implement refresh token rotation.
  • Scan application endpoints with WeSecureX XSS and Security Header tools.

Actionable Checklist

WeSecureX Platform · Surface Monitor

Application Attack Surface Analyzer

Inspect open ports, TLS certificate chains, HTTP headers, and API endpoint security posture.

Includes: TLS/SSL cipher suite & expiration monitoring · Security header verification (CSP, HSTS) · Subdomain asset discovery

Frequently Asked Questions

Because JWTs are stateless, instant revocation requires either maintaining a distributed Redis denylist of revoked `jti` (JWT ID) claims, maintaining a user token version counter in the database, or keeping token lifetimes very short (e.g., 5 minutes) so that revoking the refresh token limits exposure window.

No! JWT payloads are Base64URL-encoded, not encrypted. Anyone who intercepts or views the token can decode and read all payload claims in plaintext. For confidential data, use JSON Web Encryption (JWE) or keep sensitive data server-side.

The [WeSecureX Vulnerability Catalog](/vulnerabilities) and [Security Scanner](/scanner) inspect API responses, headers, and authentication flows for insecure cookie flags and token exposure.

Authoritative Sources & References

  • RFC 7519: JSON Web Token (JWT)IETF (official)
    View Source
  • OWASP JSON Web Token Cheat Sheet for JavaOWASP (technical)
    View Source

Continue exploring related technical architecture and defensive guides

Security Research & Engineering Articles

All articles