ThisCalculator

Everyday

JWT Vulnerabilities: Spot Unsafe Token Validation

Updated · Standard formula, shown below

The calculator for this page is switched off while it is checked. The formula is below, and the worked example in the article shows how to use it by hand.

JWT vulnerabilities come from how a server validates the token, not from the token format itself. You can spot unsafe token validation by checking three things: the algorithm is forced to a whitelist, the signature is always verified with the correct secret or public key, and the token's claims (exp, nbf, iss, aud) are strictly checked. If any of those steps are missing or attacker-influenced, the token is unsafe.

Formula

The expiration check is: exp < now + skew (reject if true). The token's exp claim is a Unix timestamp (seconds since 1970-01-01 UTC). The server compares it to its current time, optionally allowing a small clock skew (usually 30–60 seconds) to tolerate minor time differences between servers. The formula in the calculator shows the effective expiration time: exp = t + d / 60, where t is the token lifetime in minutes and d is the allowed skew in seconds. This gives you the actual cutoff in minutes after issuance.

Worked Example

Using the defaults: token lifetime = 15 minutes, clock skew = 30 seconds. Convert skew to minutes: 30 / 60 = 0.5 minutes. Expiration = 15 + 0.5 = 15.5 minutes. So the token is valid until 15 minutes and 30 seconds after issuance. Any request after that is rejected.

How JWT Validation Works

A JSON Web Token (JWT) has three parts: header, payload, signature. The header declares the algorithm (e.g., HS256, RS256). The payload contains claims like sub (subject), exp (expiration), iss (issuer), aud (audience). The signature is computed over the header and payload using a secret (HMAC) or a private key (RSA/ECDSA).

Validation is a sequence of steps:

  1. Parse the token into its three parts.
  2. Verify the signature using the expected algorithm and key.
  3. Check the exp claim (and optionally nbf, not before).
  4. Validate iss and aud against known values.
  5. Optionally check other claims like jti (token ID) for replay prevention.

If any step fails, reject the token. Unsafe validation skips or weakens one of these steps.

Common JWT Vulnerabilities

  • Algorithm confusion (RS256 to HS256): The server expects RSA but accepts HMAC. An attacker can sign a token with the public key as the HMAC secret, and the server verifies it because it uses the public key as the secret. Fix: hardcode the allowed algorithm, never take it from the token.
  • None algorithm: Some libraries accept alg: none and skip signature verification. Fix: reject any token with alg: none.
  • Weak HMAC secret: If the secret is short or guessable, an attacker can brute-force it and forge tokens. Use a random secret of at least 256 bits.
  • Missing expiration check: Some implementations forget to check exp, so tokens never expire. Fix: always validate exp and nbf.
  • Improper key handling: Using the wrong key type (e.g., symmetric for asymmetric) or not rotating keys. Fix: use separate keys for signing and verification, and rotate them regularly.
  • Claim injection: If the server trusts claims like role or admin without verifying the signature, an attacker can modify the payload. Fix: never trust payload claims without signature verification.

How to Spot Unsafe Validation in Code

Look for these red flags when reviewing JWT validation code:

  • Uses decode instead of verify (e.g., in Node.js jsonwebtoken, jwt.decode() does not verify).
  • Reads the algorithm from the token header and passes it to the verification function.
  • Does not check exp or nbf.
  • Uses a hardcoded, short, or default secret (like "secret").
  • Accepts both symmetric and asymmetric algorithms without a whitelist.
  • Does not validate issuer and audience.
  • Catches signature errors and continues processing the token (e.g., in a try/catch that ignores the error).

If you see any of these, the validation is unsafe.

Choosing a Safe Validation Strategy

Use a well-maintained JWT library and follow its recommended verification method. For example, in Node.js use jwt.verify() with an explicit algorithm list. In Python, use PyJWT with algorithms=["RS256"]. Always:

  • Set a strict algorithm whitelist.
  • Use a strong secret or key pair.
  • Check exp, nbf, iss, aud.
  • Use HTTPS to prevent token interception.
  • Keep token lifetime short (e.g., 15 minutes) and use refresh tokens for longer sessions.

Comparing JWT with Alternatives

JWT is stateless, which scales well, but it cannot be revoked easily. Opaque tokens (random strings stored server-side) can be revoked but require a database lookup. Session cookies are simpler for server-rendered apps but have CSRF concerns. JWT is best for APIs and microservices where statelessness matters, but if you need immediate revocation, consider opaque tokens or a hybrid approach.

Costs and Performance Considerations

JWT verification is fast (microseconds for HMAC, slightly more for RSA). The main cost is token size: JWTs are larger than opaque tokens, which can add overhead in HTTP headers. There is no direct monetary cost, but weak validation can lead to security breaches that are costly. Always weigh the convenience of statelessness against the need for revocation.

Safety Best Practices

  • Never log full tokens or secrets.
  • Store secrets in environment variables or a secret manager, not in code.
  • Use separate signing keys for different environments (dev, prod).
  • Rotate keys periodically and support multiple keys during transition.
  • Use HTTPS-only cookies if storing tokens in cookies.
  • Consider adding a jti claim and a denylist for logout.

Frequently Asked Questions

What is the most common JWT vulnerability?

The most common is algorithm confusion, where an attacker changes the header to alg: HS256 and signs with the public key. This works when the server does not enforce a fixed algorithm list.

Can I use the same secret for HS256 and RS256?

No. HS256 is symmetric (same secret for signing and verification), RS256 is asymmetric (private key signs, public key verifies). Using the same secret for both defeats the purpose and enables confusion attacks.

How do I check if a JWT is expired?

Compare the exp claim (Unix timestamp) with the current time. If exp < now, reject. Many libraries do this automatically when you call verify().

Is it safe to store a JWT in localStorage?

It is risky because localStorage is accessible to any JavaScript on the page, making it vulnerable to XSS. Using HttpOnly cookies is safer, but then you need CSRF protection.

What is the difference between decode and verify?

Decode only parses the token without checking the signature. Verify checks the signature and optionally the claims. Always use verify for untrusted tokens.

Can I revoke a JWT before it expires?

Not directly, because JWTs are stateless. You can maintain a denylist of jti values or shorten token lifetime. For immediate revocation, use opaque tokens.

Related

Formula used
in t | Token lifetime | min | 15 | 0
in d | Clock skew | s | 30 | 0
out exp | Expiration (minutes after now) | min | t + d / 60 | 1

Each line is one input (in, sel, list) or one result (out: label, unit, formula, decimals). How the calculators work

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.