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_tokenThe 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:
- verify the token hash;
- verify the expiration;
- verify it has not already been used;
- update the password;
- 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 asno-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.
Trusting request hostnames for email links
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:
- accept an account identifier;
- return a generic response;
- if the account exists, create a random short-lived token;
- store only its hash and metadata;
- email a link using the trusted application origin;
- verify the token once;
- update the password with the normal password-hashing policy;
- mark the token used or delete it atomically;
- revoke relevant sessions;
- 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.