Password Reset Security: Turn OWASP and NIST Rules Into Tests
Turn password reset security guidance from OWASP and NIST into practical tests for tokens, delivery, abuse controls, sessions, and reset page bypasses.

A secure password reset flow issues CSPRNG-generated, single-use tokens of at least 128 bits, expires them quickly, delivers them only through a verified channel, never confirms whether an account exists, and invalidates sessions once the reset completes. These properties line up with OWASP's and NIST's guidance, and none of them matter unless you test the full state machine end to end.
TL;DR:
- Generate tokens with a cryptographically secure generator, require 128 bits, store hashes, compare in constant time, and enforce short expiry and single use.
- Use verified email links by default; give SMS, voice codes, and typed PINs shorter expiries and stricter rate limits because they are easier to intercept or guess.
- Return the same generic response for every reset request, normalize timing, and rate limit requests and token submissions by account and IP without locking accounts.
- Require a valid token bound to the current execution before password updates, reject CSRF or stale state, and regression test replay attempts and prior bypasses.
- After a reset, invalidate all active sessions and unused recovery tokens, require normal authentication and available MFA, and notify the account owner by email.
Table of Contents
- Quick actionable checklist for reset-flow fixes
- Token generation and storage: entropy, hashing, and expiry
- Delivery channels and verification: email, SMS, and host validation
- Preventing account enumeration and reset abuse
- Hardening the reset page against state-machine bypass
- Post-reset session hygiene and user notification
- Developer testing checklist for reset flows
- Why reset flows deserve high-risk attention
- Get your reset flow verified by Earthshaker Security
- FAQ
- Sources
Quick actionable checklist for reset-flow fixes
If you have limited time before a release, work through this list first.
- Generate tokens with a CSPRNG, hash them before storage, enforce single use, and set short expiry windows.
- Send reset links to a verified email address as the default channel, and if SMS is unavoidable, shorten its expiry and tighten rate limits.
- Return the same generic message regardless of whether the account exists, and normalize response timing.
- Rate limit both reset requests and token submissions, and add CAPTCHA where abuse patterns justify it.
- Set
Referrer-Policy: no-referreron reset pages and strip third-party scripts that could leak the token. - Invalidate active sessions after a successful reset and notify the account owner by email.
Each item below gets its own section with the reasoning and the exact test to run.
Token generation and storage: entropy, hashing, and expiry
A reset token is a temporary credential, and it needs the same respect as a password. OWASP's Forgot Password cheat sheet specifies a cryptographically secure pseudorandom number generator and a minimum length of 128 bits, or 32 hex characters, to resist guessing and brute force. Anything shorter narrows the search space enough that an attacker with API access could feasibly enumerate valid tokens.
Storage matters just as much as generation. The same cheat sheet calls for hashing tokens before they touch the database, typically with SHA-256, and comparing submitted tokens in constant time. That protects you two ways: a database leak doesn't hand out live tokens, and timing differences in the comparison can't be used to guess a token byte by byte.
- Generate with a CSPRNG and never fall back to a standard random function for anything security-sensitive.
- Hash tokens before storage and use a constant-time comparison function.
- Enforce single-use semantics: invalidate a token the moment it's used or the moment a newer one is issued.
- Keep expiry windows short, erring toward the conservative end for anything delivered as a short PIN.
Delivery channels and verification: email, SMS, and host validation
Not every delivery channel earns the same trust. OWASP treats the verified primary email on file as the canonical side-channel for reset links, since it's already tied to account ownership and harder for an attacker to intercept than most alternatives.
SMS and voice OTP carry weaker guarantees: SIM swapping and carrier-level interception are real risks, so if you support that channel, shorten token expiry and apply stricter rate limits than you would for email. Manually typed PINs deserve the same caution, since a short numeric code has far less entropy than a 128-bit URL token, which means either lengthening the PIN or cutting its lifetime.
- Default to verified email links and treat SMS or PINs as secondary, weaker options.
- Shorten expiry and rate limits for SMS, voice OTP, and manually entered PINs.
- Validate the reset link's domain against a trusted list instead of trusting the
Hostheader, which prevents Host Header Injection attacks that redirect tokens to attacker infrastructure. - Set
Referrer-Policy: no-referreron the reset page so the token can't leak through theRefererheader to any embedded resource.
Pro Tip: If your reset emails include tracking pixels or third-party fonts, remove them from the reset page specifically, even if they're fine elsewhere on the site.
Preventing account enumeration and reset abuse
An attacker doesn't need to guess a password if your reset flow tells them which usernames are valid. The fix is consistency: return the exact same message whether or not the account exists, and avoid any response detail, including subtle wording differences, that gives it away.
Timing is a second leak path. OWASP's WSTG documents how response time differences between "account exists, email sent" and "account doesn't exist, no email sent" can be measured and exploited, so sending email asynchronously or normalizing response time closes that gap.
- Return one generic message for every reset request, regardless of account status.
- Normalize response timing or send email asynchronously to avoid timing-based enumeration.
- Apply per-account and per-IP rate limits on both reset requests and token submissions, with CAPTCHA as a backstop.
- Avoid locking accounts in response to repeated reset attempts, since that turns the flow into a tool for denial-of-service against your own users.
Hardening the reset page against state-machine bypass
Token entropy and rate limits don't help if an attacker can skip the token check entirely. A documented case, Keycloak CVE-2026-18963, showed a reset-flow state-machine bypass that let an attacker reach the password-update step without a valid email action token at all. The fix bound the request to its execution id and enforced the token check before that step could run.
The underlying lesson generalizes: a reported state-machine issue in reset flows notes that without strict state enforcement, an attacker can reach "Update Password" by replaying or manipulating requests rather than completing the intended sequence.
- Require a valid action token bound to the current execution or session before allowing the password-update step.
- Apply CSRF protection and reject any request missing the expected state.
- Strip third-party scripts and resources from the reset page, and keep
Referrer-Policy: no-referrerin place.
Pro Tip: Write a regression test for every state-machine bypass you find, since these bugs tend to resurface after unrelated refactors.
Post-reset session hygiene and user notification
A successful password reset should close every door an attacker might have opened, not just the one they used. OWASP guidance calls for invalidating existing sessions and any outstanding recovery tokens the moment a reset completes, which removes an attacker's ability to stay logged in on a session they hijacked earlier.
- Invalidate all active sessions and unused recovery tokens once the password changes.
- Skip auto-login after a reset; require the normal authentication and MFA steps where available, since WSTG is explicit that reset flows must not bypass MFA.
- Send a notification confirming the reset, with a way for the account owner to report activity they didn't initiate.
- Require re-authentication before changing a primary email, phone number, or MFA backup method, so a reset can't be chained into altering recovery data.
Developer testing checklist for reset flows
Build these checks into your test suite, not just your launch-day review.
- Confirm tokens come from a CSPRNG, measure at least 128 bits, and are stored only as a hash compared in constant time.
- Test for account enumeration through response content, timing, and side effects like whether an email was sent, per WSTG.
- Attempt state-machine bypasses, including replayed requests and stale execution ids, and confirm the known bypass pattern is blocked.
- Test Host header tampering, CSRF protections, and Referer leakage from any third-party resource on the reset page.
- Automate regression tests for previously fixed issues and fold reset flows into your authenticated penetration test scope.
Pro Tip: Keep a running list of every reset-flow bug your team has ever found, and turn each one into a permanent regression test instead of a one-time fix.
Why reset flows deserve high-risk attention

Reset flows exist specifically to bypass your normal login, which makes them an attractive shortcut for anyone trying to take over an account rather than guess a password. That's the paradox: the feature built to help locked-out users is also the feature most likely to let an attacker in.
Automated scanners catch missing headers and obvious misconfigurations, but state-machine regressions, like the Keycloak case above, live in application logic that a scanner can't reason about. Those need a person stepping through the flow with intent to break it. Any serious regression suite or penetration test scope should treat password reset as its own line item, not an afterthought bundled into general authentication testing.
— caleb
Get your reset flow verified by Earthshaker Security
Checklists catch the obvious gaps, but state-machine bugs like the Keycloak case hide in logic that only shows up under manual testing. We run authenticated web application penetration tests, API and endpoint testing, and configuration audits that specifically walk through reset-flow logic, token handling, and session invalidation rather than stopping at a surface scan.

We pair automated scanning with hands-on verification from a tester you can talk to directly, and every confirmed finding comes with a fix you can hand to your engineers plus a re-test once it's closed. Engagements start at our Starter tier, with Pro, Retainer, and Custom engagement options for ongoing coverage. Check current scopes and pricing on our pricing page to get your reset flow tested before someone else finds the gap first.
FAQ
How do I clean up my password?
Cleaning up a password means replacing anything reused across sites with a unique one, ideally generated and stored in a password manager rather than memorized. NIST SP 800-63B-4 recommends checking new passwords against a blocklist of commonly used or compromised passwords instead of relying on arbitrary complexity rules.
How do I reset my Gmail password?
Gmail's own account recovery flow handles this through Google's account settings, where you verify ownership via your recovery email or phone before setting a new password. Any legitimate reset flow, including Google's, should never ask for your current password over email or text.
How do I change a security password in Windows 10?
Windows lets you change your account password through Settings under Accounts, then Sign-in options, where you can update it directly if you're already logged in. If you're locked out entirely, Windows routes you through its own account recovery process tied to your Microsoft account.
How do I remove a security password?
Removing a password outright is rarely advisable for any account holding personal or financial data, since it eliminates a layer of protection rather than improving it. If you're trying to simplify sign-in, a password manager or passkey is a safer route than removing authentication altogether.
What makes a password reset token secure?
A secure reset token is generated by a CSPRNG, measures at least 128 bits, is stored as a hash rather than plaintext, and expires quickly after a single use, according to OWASP's Forgot Password guidance. Anything shorter, reusable, or stored in plaintext falls short of that standard.
Sources
- Forgot Password - OWASP Cheat Sheet Series
- SP 800-63B-4, Digital Identity Guidelines: Authentication and Authenticator Management | CSRC
- Keycloak CVE-2026-18963: account takeover with no email in the loop | Exploitmatic
Recommended
Want this run against your app?
Book a scanComments
No comments yet. Questions, corrections and war stories all welcome.