Auditeur de Sécurité JWT

Auditez les tokens JWT pour vulnérabilités d'algorithme et claims manquants

What is JWT Security Auditing?

JWT (JSON Web Token) security auditing is the systematic process of analyzing token configurations, signing algorithms, and claim structures to identify vulnerabilities that could allow unauthorized access, token forgery, or privilege escalation. While tools like JWT decoders parse tokens and JWT verifiers check signatures, a security auditor evaluates the overall security posture of a JWT implementation — detecting misconfigurations that remain invisible to basic validation.

The JWT specification (RFC 7519) and the JSON Web Algorithms specification (RFC 7518) provide flexibility in algorithm selection and claim usage. This flexibility is a double-edged sword: it enables diverse use cases but also introduces attack surfaces when implementers choose weak configurations. A JWT security auditor flags these weaknesses before attackers exploit them — covering algorithm confusion attacks, weak signing keys, missing critical claims, and insecure header parameters that standard validators silently accept.

Critical JWT Vulnerability Types

JWT implementations are susceptible to several well-documented attack vectors. A thorough security audit checks for each of these vulnerabilities:

  • Algorithm "none" attack (CVE-2015-9235): The JWT spec allows "alg": "none" for unsecured tokens. Attackers modify a signed token's header to {"alg": "none"} and strip the signature. Libraries that honor the header's algorithm claim without server-side enforcement will accept the forged token as valid — granting unauthorized access without any cryptographic verification.
  • HMAC/RSA confusion (CVE-2016-10555): When a server expects RSA-signed tokens (RS256) but a library also accepts HMAC (HS256), an attacker can sign a forged token using the server's public RSA key as the HMAC secret. Since the public key is available to everyone, this effectively allows anyone to forge valid tokens. The audit detects tokens using symmetric algorithms where asymmetric signing is expected.
  • Weak HMAC signing keys: HMAC-SHA256 requires a key with at least 256 bits of entropy to resist brute-force attacks. Keys derived from short passwords, dictionary words, or predictable values (like "secret" or "password123") can be cracked offline using tools like hashcat. The auditor estimates key entropy and flags keys shorter than the algorithm's hash output.
  • Missing expiration claims: Tokens without an exp (expiration) claim remain valid indefinitely. If compromised through log leakage, stolen cookies, or XSS, they provide permanent unauthorized access. The auditor flags tokens lacking exp, nbf (not before), and iat (issued at) temporal claims.
  • Unvalidated claims: Claims like sub (subject), iss (issuer), and aud (audience) must be validated server-side. Tokens with overly permissive audience values, missing issuer constraints, or user-controlled claims that feed into authorization decisions represent privilege escalation risks.

Security Best Practices for JWT Implementation

Beyond detecting vulnerabilities, a JWT security audit validates that implementation follows established cryptographic best practices:

  • Explicit algorithm allowlist: Never rely on the token's alg header to select the verification algorithm. Maintain a server-side allowlist of accepted algorithms and reject tokens specifying anything outside that list. This prevents both the "none" attack and algorithm confusion entirely.
  • Asymmetric signing for distributed systems: Use RS256, RS384, ES256, or EdDSA for systems where multiple services verify tokens but only one service issues them. Asymmetric algorithms ensure that verifiers (holding only the public key) cannot forge tokens — eliminating the shared-secret distribution problem of HMAC.
  • Short token lifetimes: Set exp to the shortest duration acceptable for your use case — typically 5 to 15 minutes for access tokens. Use refresh tokens (stored server-side) to issue new access tokens without requiring re-authentication. Short lifetimes limit the damage window if a token is compromised.
  • Audience and issuer validation: Always verify aud matches your service identifier and iss matches your expected identity provider. Without these checks, a token issued for Service A could be replayed against Service B if both share the same signing key.
  • Key rotation and kid header: Rotate signing keys periodically and use the kid (Key ID) header to identify which key signed a given token. This enables zero-downtime key rotation — new tokens use the new key while existing tokens remain verifiable with the old key until they expire.
  • Avoid sensitive data in payloads: JWT payloads are Base64URL-encoded, not encrypted. Anyone with access to the token can decode and read all claims. Never include passwords, credit card numbers, personal health information, or cryptographic secrets in JWT payloads. Use JWE (JSON Web Encryption) if payload confidentiality is required.

Audit Checklist: What the Security Auditor Evaluates

A comprehensive JWT security audit evaluates tokens across multiple dimensions. This tool performs the following automated checks on any JWT you provide:

  1. Algorithm strength assessment: Classifies the alg header value into categories — insecure (none), deprecated (HS256 with weak key), acceptable (RS256, HS256 with strong key), or recommended (ES256, EdDSA). Flags tokens using algorithms with known weaknesses.
  2. Temporal claim analysis: Verifies presence of exp, iat, and nbf. Calculates token lifetime and flags excessively long durations (greater than 24 hours for access tokens). Checks that nbf precedes exp and that iat is not in the future.
  3. Claim completeness scoring: Evaluates whether essential claims (iss, sub, aud, jti) are present. Missing claims reduce the security score and generate specific recommendations for what to add and why.
  4. Header parameter inspection: Detects dangerous header parameters like jku (JWK Set URL) and x5u (X.509 URL) that point to attacker-controlled key sources. Flags jwk embedded keys that bypass key management entirely.
  5. Payload sensitivity scan: Identifies claims that may contain sensitive data (email addresses, internal IDs, role arrays) and warns about information exposure in unencrypted tokens.

The auditor produces a security score from 0 to 100 along with prioritized recommendations, enabling developers to systematically harden their JWT implementation before deploying to production.

JWT Security Auditor vs Related JWT Tools

The JWT ecosystem includes several tools with distinct purposes. Understanding the difference prevents gaps in your security workflow:

  • JWT Decoder: Parses the token structure and displays the header and payload as readable JSON. Does not evaluate security — a decoder happily displays a token signed with "alg": "none" without raising any concern.
  • JWT Generator: Creates new tokens with specified claims and algorithm. Useful for testing but does not assess whether the chosen configuration is secure.
  • JWT Verifier: Checks that a token's signature is valid against a provided key. Answers "was this token signed by this key?" but not "is this token configuration safe?"
  • JWT Security Auditor (this tool): Evaluates the security posture of a token's configuration. Answers "what are the vulnerabilities in this token?" and "how do I fix them?" Combines algorithm analysis, claim assessment, header inspection, and best-practice validation into a single comprehensive security report.

In a mature security workflow, all four tools serve complementary roles: generate tokens with proper configuration, decode them for debugging, verify signatures in production, and audit the overall security posture during development and code review.

Code Examples

JWT Security Audit: Detecting Vulnerabilities in Token Configuration

// JWT Security Audit — detect common vulnerabilities
function auditJwtSecurity(token) {
  const [headerB64, payloadB64] = token.split('.');
  const header = JSON.parse(atob(headerB64));
  const payload = JSON.parse(atob(payloadB64));
  const findings = [];

  // Check 1: Algorithm "none" attack
  if (header.alg === 'none' || header.alg === 'None') {
    findings.push({
      severity: 'critical',
      issue: 'Algorithm set to "none" — token has no signature',
      fix: 'Use RS256, ES256, or EdDSA with server-side algorithm enforcement'
    });
  }

  // Check 2: Weak symmetric algorithms without key strength context
  const weakAlgorithms = ['HS256', 'HS384', 'HS512'];
  if (weakAlgorithms.includes(header.alg) && header.kid === undefined) {
    findings.push({
      severity: 'warning',
      issue: `Symmetric algorithm ${header.alg} without key ID (kid)`,
      fix: 'Use asymmetric signing (RS256/ES256) or add kid for key rotation'
    });
  }

  // Check 3: Missing expiration claim
  if (!payload.exp) {
    findings.push({
      severity: 'high',
      issue: 'Missing "exp" claim — token never expires',
      fix: 'Add exp claim with short lifetime (300-900 seconds for access tokens)'
    });
  }

  // Check 4: Excessive token lifetime
  if (payload.exp && payload.iat) {
    const lifetimeHours = (payload.exp - payload.iat) / 3600;
    if (lifetimeHours > 24) {
      findings.push({
        severity: 'medium',
        issue: `Token lifetime is ${lifetimeHours.toFixed(1)} hours (>24h)`,
        fix: 'Reduce to 15 minutes for access tokens, use refresh tokens'
      });
    }
  }

  // Check 5: Missing issuer/audience claims
  if (!payload.iss) {
    findings.push({
      severity: 'medium',
      issue: 'Missing "iss" claim — cannot verify token origin',
      fix: 'Add iss claim matching your identity provider URL'
    });
  }
  if (!payload.aud) {
    findings.push({
      severity: 'medium',
      issue: 'Missing "aud" claim — token accepted by any service',
      fix: 'Add aud claim with target service identifier'
    });
  }

  // Check 6: Dangerous header parameters
  const dangerousHeaders = ['jku', 'x5u', 'jwk'];
  for (const param of dangerousHeaders) {
    if (header[param]) {
      findings.push({
        severity: 'critical',
        issue: `Header contains "${param}" — external key injection risk`,
        fix: `Remove ${param} and use server-side key resolution via kid`
      });
    }
  }

  // Calculate security score (100 = no issues found)
  const severityWeights = { critical: 30, high: 20, medium: 10, warning: 5 };
  const deductions = findings.reduce(
    (sum, f) => sum + (severityWeights[f.severity] || 5), 0
  );
  const score = Math.max(0, 100 - deductions);

  return { score, findings, algorithm: header.alg, claims: Object.keys(payload) };
}

// Example usage
const token = 'eyJhbGciOiJub25lIn0.eyJzdWIiOiIxMjM0In0.';
const audit = auditJwtSecurity(token);
// → { score: 20, findings: [{severity: 'critical', issue: 'Algorithm set to "none"'...}], ... }

Standards & Specifications

  • RFC 7519 — JSON Web Token (JWT) specification — defines token structure, registered claims, and processing rules
  • RFC 7518 — JSON Web Algorithms (JWA) — specifies cryptographic algorithms for JWS and JWE including HMAC, RSA, and ECDSA
  • RFC 7515 — JSON Web Signature (JWS) — defines the signature mechanism and header parameters used in signed JWTs
  • RFC 8725 — JWT Best Current Practices — security recommendations addressing known JWT vulnerabilities and attack vectors