Password Reset Token Security Best Practices

Password reset mechanisms are among the most targeted entry points for account takeover attacks in modern web applications. If an attacker can predict, intercept, or manipulate a reset token, they bypass standard authentication entirely. Securing this flow requires strict controls across the entire token lifecycle: token generation, secure storage, transport, and validation.

Cryptographically Secure Generation

A password reset token must be impossible to guess or brute-force.

  • Use CSPRNGs: Always generate tokens using a Cryptographically Secure Pseudo-Random Number Generator (such as crypto.randomBytes in Node.js, secrets in Python, or SecureRandom in Java). Standard pseudo-random functions like Math.random() or rand() are predictable and must never be used.
  • Ensure High Entropy: Tokens should contain at least 128 bits (16 bytes) of entropy, commonly represented as a 32-character hexadecimal string or a 43-character URL-safe Base64 string.
  • Avoid Identifiers in Token Payloads: Do not generate tokens by simply hashing known or guessable user data, such as usernames, email addresses, creation dates, or sequential IDs.

Secure Storage and Invalidation Rules

Treating a reset token like an active credential prevents database compromises from becoming immediate account takeovers.

  • Hash Tokens at Rest: Store only a cryptographic hash (such as SHA-256) of the token in your database. When a user presents the raw token via the reset link, hash the input and compare it to the database record. If your database is leaked, an attacker cannot immediately use the stored values to hijack accounts.
  • Enforce Single-Use: Invalidate the token immediately after it is consumed. Allowing a token to be reused leaves accounts vulnerable to browser history snooping or transport-layer interception.
  • Invalidate on New Requests: If a user requests multiple password resets, previous tokens issued to that account should be automatically revoked, ensuring only the latest token is valid.
  • Invalidate on Password Change: Once a password update succeeds, revoke all active sessions for the user and invalidate any outstanding reset or email verification tokens.

Time-Based Constraints and Expiration

Reset tokens should have the shortest practical lifespan to minimize the window of opportunity for an attacker.

  • Short Expiration Windows: Limit token validity to 15 to 60 minutes. Longer lifespans unnecessarily increase risk without providing significant user experience benefits.
  • Explicit Timestamp Validation: Check the token’s creation timestamp against the current server time during validation, immediately rejecting any expired token before performing any database updates.

Transport and Leakage Mitigation

The way tokens are delivered and handled in the browser requires defensive measures against leakage.

  • Mitigate Referrer Leakage: Reset links sent via email point to a web page where the token is typically passed as a URL parameter. If the page loads external assets or links out to external domains, the token can leak via the Referer header. Add Referrer-Policy: no-referrer headers to prevent this.
  • Timing-Attack Resistance: When validating the token string, use a constant-time comparison algorithm (such as crypto.timingSafeEqual) to prevent attackers from determining correct characters via execution-time differences.
  • Prevent User Enumeration: The initiation endpoint should return a generic response (e.g., "If an account exists, an email has been sent") regardless of whether the submitted email address is registered, preventing attackers from mapping out valid users.