Safeguard
Application Security•

ASVS 5.0 V9.2.1: Enforce Token Validity Windows (nbf and exp)

ASVS V9.2.1 (Level 1): if a token has a validity period, only accept it inside that window. Check exp and nbf, set small clock skew, and test with expired tokens.

Safeguard Security Team
2 min read

ASVS 5.0 V9.2.1 says that when a token carries a validity time span, you accept the token only if the verification time falls inside it. For JWTs that means checking nbf (not before) and exp (expiry). It is a Level 1 requirement.

Why it matters

An expiry that nobody enforces is a token that lives forever. A leaked token from a log file, a screenshot or an old backup stays useful to the attacker for exactly as long as your verifier lets it.

How to implement it

Most libraries check exp and nbf by default when present, but two things go wrong: someone switches the check off, or the claim is simply missing and the token is accepted as never expiring. Require the claims.

await jwtVerify(token, key, {
  algorithms: ["ES256"],
  issuer: ISSUER,
  audience: AUDIENCE,
  requiredClaims: ["exp", "iat"],
  clockTolerance: 30,       // seconds of allowed clock skew
  maxTokenAge: "15m",       // also bound by issue time
});
jwt.decode(token, key, algorithms=["ES256"], audience=AUD, issuer=ISS,
           leeway=30, options={"require": ["exp", "iat", "nbf"]})

Keep access token lifetimes short, minutes rather than days, and use refresh tokens with their own controls for longer sessions. Sync server clocks with NTP, and keep tolerance in seconds.

How to test it

  1. Mint a token with exp one minute in the past. Expect 401.
  2. Mint one with nbf in the future. Expect 401.
  3. Mint one with no exp. Expect 401.
  4. Test the boundary: a token 10 seconds past expiry passes with 30 seconds of tolerance, one 60 seconds past does not.
  5. Search for ignoreExpiration, verify_exp: False and similar flags.

Common mistakes

  • Treating a missing exp as valid.
  • Large clock tolerance such as hours "to fix clock skew".
  • Expiry in milliseconds where seconds are expected, giving tokens valid for thousands of years.
  • Disabling expiry checks in tests and shipping the same config.

Quick check

A token has no exp claim. Should the API accept it?

Not unless you have a documented reason. Require exp so every token has an end.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.