Under attack? Get incident response help

Identity and access

Password reset flows that do not undo your login security

How to design and test a forgot-password flow: tokens, expiry, enumeration, sessions, and a reset that cannot bypass an enrolled second factor.

Also in:TürkçeDeutschFrançaisEspañolItalianoPolskiNederlandsEesti

Illustration: Password reset flows that do not undo your login security

A password reset is a login that skips the password, so every control on the login form has to be matched or deliberately replaced there. If the reset path is weaker than the login path, an attacker will use it. The OWASP Forgot Password Cheat Sheet calls the function "a common source of vulnerabilities", with user enumeration as the best-known example.

Why the reset flow is the weak point

A successful login produces a session. According to the OWASP session management guidance, an authenticated session ID is temporarily equivalent to the strongest authentication method the application used. A reset that ends in a session, or in a password the attacker chose, hands over the same thing.

NIST SP 800-63B-4 requires proof of possession and control of two distinct authentication factors at AAL2. If the login asks for two factors, a reset that needs one emailed token is a single-factor way around it. The excerpt we rely on does not cover recovery; the full document has a section on authenticator event management.

Our own rule: on an account with an enrolled second factor, ask for that factor or a saved recovery code before the new password is accepted, and never let a reset remove or replace the factor.

The request step

Answer identically whether or not the account exists. "If that address is registered, we have sent an email" is enough.

Timing leaks too. A handler that exits early for unknown addresses and sends mail for known ones shows the difference whatever the message says. Run the same logic for every request, or hand the mail to an asynchronous job.

Add per-account rate-limiting, a CAPTCHA or a similar control. Without one, an attacker can fire thousands of reset requests per hour at one account and bury the victim's inbox or SMS. Do not lock or change the account on request.

The token

OWASP asks four things of a token or code:

  • it is generated with a cryptographically safe random algorithm;
  • it is long enough to defeat brute force;
  • it is stored securely;
  • it is single use and expires after an appropriate period.

The sheet gives no figures for length or lifetime. The nearest number in the sources is in the session guidance: a CSPRNG with at least 128 bits of entropy, 160 bits or more preferred. It is written for session tokens; we apply it to reset tokens. For lifetime, choose a short value, write it down and test against it.

"Stored securely" can be read strictly. If read-only disclosure of the token store is in your threat model, the session guidance describes an identifier and secret verifier split. The server keeps the public lookup identifier and the full SHA-256 hash of the verifier, and compares hashes in constant time. The identifier alone must never count as proof. Applying this to reset tokens is our suggestion.

Deliver the token through a side channel, email or SMS, never in the browser's response. A token in the URL is the simplest design, but the session guidance warns that IDs in URLs can leak through links and logs, browser history and bookmarks, the Referer header and search engines. Keeping third-party content and outbound links off the reset page closes the Referer leak only. For the rest, exchange the token on the first request for short-lived server-side state, redirect to a clean URL, and keep that first query string out of access logs.

The reset step

  • Have the user type the new password twice, and check it against the same policy as registration.
  • Hash it through the same code path as every other password.
  • Invalidate every outstanding reset token for the account when the password or the email address changes. Otherwise an older mail still sitting in an inbox can reset the account again.
  • Email the user to say the password changed. Never put the password in the message.
  • Send the user to the normal login instead of signing them in. Automatic login adds session-handling code, and with it the chance of new vulnerabilities.
  • End existing sessions, or at least offer to.

After a suspected compromise, the attacker's live session survives a password change unless you end it. We would make invalidation the default.

MFA and compromised accounts

A password reset alone may not restore control. The attacker may still hold an active session, or may have changed recovery information or MFA methods.

OWASP says not to rely solely on recently added or changed recovery information. Use independent evidence established earlier, such as saved recovery codes, in a combination that meets the account's assurance requirements. Resetting MFA has its own section in OWASP's Multifactor Authentication Cheat Sheet, so keep that process separate from the password flow.

The OWASP Authentication Cheat Sheet also covers changing a registered email address, which matters because a changed address redirects every future reset.

Testing the flow

TestWhat a pass looks like
Real and non-existent addressSame message and status code
Scripted batch of requests per addressMedian response times match
Repeated requests for one accountRate limit or CAPTCHA triggers
Request a reset, then log inAccount not locked or altered
Token used twice, or after expiryAttempt fails
Old token after a password or email changeAttempt fails
Reset page with the token in the URLNo third-party requests or outbound links
Reset with an old session openOld session ended
Enrolled second factor, emailed token only (our rule)Reset refused
Final response and the mailNo automatic login, no password in the mail

For the timing row, compare medians and spread across many runs. This belongs in any penetration test of an authentication flow.

Where to start

  1. Request a reset for an existing and a non-existent account many times each. Compare message, status code and response time.
  2. Read the token generator. Confirm it uses a CSPRNG, make tokens single-use, and set a lifetime you can state in minutes.
  3. Add per-account rate-limiting or a CAPTCHA to the request endpoint, and confirm a request never locks or modifies the account.
  4. Make the reset end other sessions, invalidate outstanding tokens, and send a notification email without the password. Check that the reset page loads nothing from third parties.
  5. Take a test account with MFA enrolled and try to reset it using only the emailed token. Confirm the reset is refused.
  6. Check who can reset or remove a second factor, what evidence they must present, and whether recovery treats recently changed details as untrusted.

How we can help

Penetration testing

We attack your networks, applications and people the way a real adversary would, then show you how to close each gap.

Contact us
AngelForce team

Written by the AngelForce team: security consultants with CEH, LPT Master and CISA certifications who test, monitor and defend systems for organisations across Europe.

Written with AI assistance from public sources listed below, and reviewed by the AngelForce team before publication.

Sources

  1. cheatsheetseries.owasp.org/cheatsheets/Forgot_Password_Cheat_Sheet.html
  2. cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
  3. pages.nist.gov/800-63-4/sp800-63b.html
  4. cheatsheetseries.owasp.org/cheatsheets/Authentication_Cheat_Sheet.html

Articles about Identity and access

Tell us what you need to protect

Describe your environment and what you want to achieve. We will reply by email.

Contact us
info@angelforce.eu +372 5705 2044

AngelForce OÜ · Tallinn, Estonia