Password reset is an authentication mechanism. Anyone who can complete the reset flow can usually take control of the account, so recovery deserves protections comparable to login.

A secure design must prevent token guessing, account enumeration, replay, accidental disclosure, and long-lived takeover opportunities.

Return the same public response

A reset form often accepts an email address or username. Do not reveal whether that identifier exists.

Prefer a response such as:

If an account matches that address, reset instructions will be sent.

Use the same HTTP status and a reasonably similar response path for existing and non-existing accounts.

Perfect timing equality is difficult, but avoid obvious branches such as performing synchronous email delivery only for valid accounts. This keeps the endpoint from becoming a convenient account-discovery API.

Generate a high-entropy token

Reset tokens should come from a cryptographically secure random generator.

A typical design uses at least 128 bits of unpredictable entropy. Thirty-two random bytes provides a large safety margin before encoding.

A simple server-side design is:

raw_token = random_bytes(32)
token_hash = SHA-256(raw_token)

store(
  token_hash,
  user_id,
  expires_at,
  used=false
)

email URL containing raw_token

The raw token goes to the user. The database stores only a verifier such as a cryptographic hash.

If the token table is leaked, hashed random tokens are harder to use directly than plaintext bearer tokens.

Keep tokens short-lived and single-use

A reset link should expire quickly enough to limit exposure but long enough for normal email delivery and user action.

The exact duration depends on the product. Values measured in minutes rather than days are common.

On successful reset:

  1. verify the token hash;
  2. verify the expiration;
  3. verify it has not already been used;
  4. update the password;
  5. invalidate the token in the same logical operation.

A token that remains valid after use can be replayed.

Database transactions or atomic state transitions help prevent two concurrent requests from successfully consuming the same token.

Rate-limit both request and verification paths

Rate limiting reduces abuse but should not be the only defense.

Consider limits by:

  • source IP or network;
  • normalized account identifier;
  • device or risk signal;
  • overall system volume.

Avoid a single extremely strict IP limit because shared networks can place many legitimate users behind one address.

The token itself must remain unguessable even if rate limiting fails.

Build reset URLs from trusted configuration

Do not construct reset links from an untrusted Host header.

Instead, use a configured canonical origin such as:

https://example.com/reset?token=...

This avoids host-header injection that can send a valid token to an attacker-controlled domain.

Keep reset tokens out of third-party analytics parameters and logs.

Reduce token leakage in the browser

Reset tokens can leak through URLs, browser history, screenshots, referrer headers, or monitoring tools.

Useful controls include:

  • HTTPS for every recovery page;
  • a strict Referrer-Policy, such as no-referrer, on token-bearing pages;
  • avoiding third-party scripts on the reset page;
  • redacting query strings in application and proxy logs;
  • exchanging the URL token for a short-lived server-side reset session when appropriate.

Do not assume that hiding a token from the visible page automatically keeps it out of infrastructure logs.

Validate the new password normally

Password reset should enforce the same password policy as account creation or password change.

Do not silently truncate passwords.

Store passwords using a password-specific hashing function such as Argon2id, scrypt, or bcrypt with parameters appropriate for the deployment. Never store plaintext passwords or use a fast general-purpose hash as the password database format.

Decide what happens to existing sessions

After a password reset, existing authenticated sessions may still represent an attacker who already logged in.

A strong default is to revoke other sessions and refresh tokens, or at minimum give the user a clear option to do so.

High-risk products may revoke all sessions automatically.

Also consider rotating recovery codes or other authentication state when compromise is suspected.

Notify the account owner

Send a separate notification after a successful password reset.

The message should say that the password changed and explain how to contact support or secure the account if the user did not perform the action.

Do not include the new password.

A notification does not prevent compromise, but it shortens the time to detection.

Avoid security questions as the primary recovery secret

Questions such as a birth city or school name are often guessable, discoverable, or reused.

Prefer possession-based recovery channels, recovery codes, verified support procedures, or stronger recovery mechanisms appropriate to the service.

If manual support can bypass normal recovery, that support process becomes part of the authentication boundary and needs strong identity verification and audit logging.

Common mistakes

Storing reset tokens in plaintext

Store a verifier such as a cryptographic hash while sending the raw bearer token only to the user.

Allowing multiple successful uses

Make tokens single-use and invalidate them atomically with the password update.

Logging full reset URLs

URLs can contain bearer credentials. Redact tokens at application, proxy, observability, and support layers.

Use an allowlisted or configured canonical origin.

Revealing whether an account exists

Use a uniform public response for reset requests.

Forgetting active sessions

Password recovery can be incomplete if an attacker-controlled session remains valid afterward.

A safer end-to-end flow

A practical reset sequence is:

  1. accept an account identifier;
  2. return a generic response;
  3. if the account exists, create a random short-lived token;
  4. store only its hash and metadata;
  5. email a link using the trusted application origin;
  6. verify the token once;
  7. update the password with the normal password-hashing policy;
  8. mark the token used or delete it atomically;
  9. revoke relevant sessions;
  10. notify the user of the completed change.

Password reset should be designed as an authentication ceremony, not as a convenience email. The recovery path is only as strong as its token lifecycle, transport, logging, and post-reset session policy.