Password reset links look simple.
A user forgets the password, enters an email address, receives a reset link, clicks it, and creates a new password.
But from a security point of view, password reset is one of the most sensitive parts of authentication security.
A typical reset link looks like this:
https://example.com/reset-password?token=abc123xyz
`
That token is not just a random value.
It is a temporary key to the user’s account.
If an attacker steals that token before it expires, they may be able to reset the password and take over the account.
That is why password reset should never be treated as a small convenience feature. It is part of the authentication system.
The Real Problem Is Not Only the Token
Many developers focus only on whether the reset token is random.
That is important, but it is not enough.
A token can be long, random, and hard to guess, but the reset flow can still be insecure if the application leaks that token somewhere else.
A reset token in a URL may be exposed through:
- Browser history
- Shared devices
- Server logs
- Reverse proxy logs
- SIEM platforms
- Third-party analytics tools
- Email forwarding
- Mobile app logs
- Browser extensions
- Referer headers
So the real question is not only:
Is the token strong?
The better question is:
Where can this token travel after the user clicks the link?
Why HTTPS Alone Is Not Enough
A common developer mistake is assuming:
“We use HTTPS, so the reset link is safe.”
HTTPS is necessary, but it does not solve every password reset risk.
HTTPS protects the token while it is moving between the browser and the server. But after the URL reaches the browser, it can still be stored, logged, forwarded, or leaked.
HTTPS does not stop:
- The URL from being saved in browser history
- Server logs from recording the full reset URL
- Analytics tools from collecting page URLs
- A user from forwarding the email
- A proxy or SIEM platform from storing the URL
- The browser from sending the URL in a Referer header
This is why password reset security needs layered protection.
HTTPS is required, but HTTPS alone is not enough.
A Realistic Token Leakage Scenario
One practical issue is Referer header leakage.
Here is how it can happen:
- A user receives a password reset link.
- The link contains a token in the URL.
- The user opens the reset page.
- The reset page loads a third-party analytics script, image, font, or tracking pixel.
- The browser sends the full reset URL in the Referer header.
- The third-party service receives the token.
- If the token is still valid, an attacker may use it to reset the account password.
In this case, the attacker does not guess the token.
The application leaks it.
That is an important security lesson:
A strong token can still become dangerous if the application exposes it.
What Security Testers Usually Check
During password reset testing, security testers and bug bounty hunters usually check more than token randomness.
They check things like:
- Is the token single-use?
- Does the token expire quickly?
- Can old tokens still work?
- Is rate limiting enabled?
- Are reset attempts logged safely?
- Are tokens leaked in application logs?
- Are tokens leaked through Referer headers?
- Are third-party scripts loaded on reset pages?
- Can open redirects steal the token?
- Can Host header poisoning happen?
- Are active sessions invalidated after reset?
- Is MFA required for sensitive accounts?
A password reset bug becomes serious when it leads to account takeover.
Password Reset Poisoning
Password reset poisoning happens when an attacker tricks the application into generating a reset link with an attacker-controlled domain.
For example, if an application blindly trusts the Host header, an attacker may cause the system to generate a link like this:
text
https://attacker.com/reset-password?token=real-reset-token
If the victim receives the email and clicks the link, the token may be sent to the attacker’s domain.
To prevent this, applications should:
- Use a fixed trusted domain for reset links
- Avoid building reset URLs from untrusted headers
- Validate allowed domains
- Reject suspicious Host headers
- Monitor unusual reset requests
This is a small implementation detail, but it can create a serious account takeover risk.
Open Redirect Token Theft
Open redirects can also become dangerous when combined with password reset links.
Example:
text
https://example.com/reset-password?token=abc123&next=https://evil.com
If the application redirects the user to the next URL without proper validation, the reset token may leak through the redirect flow or the Referer header.
Open redirects may look low-risk alone.
But when chained with password reset token leakage, they can become serious.
To reduce this risk:
- Avoid open redirects
- Use allowlisted redirect destinations
- Never pass reset tokens to external URLs
- Remove sensitive tokens before redirecting
- Use strict Referrer-Policy headers
Why Reset Tokens Should Be Hashed
Reset tokens should not be stored in plaintext.
Bad example:
text
user_id: 101
reset_token: abc123xyz
expires_at: 2026-05-20 10:30
If the database is leaked, attackers can use those raw reset tokens directly.
A safer design is to store only a hash of the token.
Better example:
text
user_id: 101
reset_token_hash: 5f2a9c...
expires_at: 2026-05-20 10:30
The raw token is sent to the user.
The database stores only the hash.
When the user clicks the reset link, the backend hashes the received token and compares it with the stored hash.
This is similar to why passwords should be stored as hashes instead of plaintext.
Why Old Tokens Should Stop Working
If a user requests multiple password reset links, older links should become invalid.
Example:
- User requests a reset link at 10:00.
- User requests another reset link at 10:05.
- The first token should no longer work.
If old tokens remain valid, an attacker may use an older email or leaked token to reset the account.
A secure system should either allow only the latest reset token to work or invalidate all previous tokens when a new reset request is created.
Why Sessions Should Be Invalidated After Password Reset
After a password reset, active sessions should usually be invalidated.
Changing the password does not always remove existing sessions.
If an attacker is already logged in, they may stay logged in even after the user resets the password.
That creates a dangerous situation:
The user thinks the account is secure, but the attacker still has an active session.
For sensitive systems, all active sessions should be revoked after password reset.
For normal applications, users should at least get a clear option to log out from all devices.
Password reset and session management should work together.
Rate Limiting Matters
Forgot password endpoints are often abused.
Without rate limiting, attackers can:
- Send repeated reset emails
- Test whether email addresses are registered
- Abuse reset APIs
- Attempt token brute force
- Flood user inboxes
- Automate attacks against many accounts
A secure system should rate limit:
- Reset requests per email address
- Reset requests per account
- Reset requests per IP address
- Token verification attempts
- Failed reset attempts
CAPTCHA can help, but it should not be the only defense.
Attackers can use CAPTCHA-solving services, proxies, automation tools, and bot networks.
Rate limiting must be enforced on the backend.
Mobile App Deep Link Risks
Mobile apps often use deep links for password reset.
Example:
text
myapp://reset-password?token=abc123xyz
or:
text
https://example.com/reset-password?token=abc123xyz
Mobile reset flows can introduce additional risks:
- Another app may intercept weakly configured deep links
- Debug logs may store reset URLs
- Crash reporting tools may capture sensitive URLs
- Mobile analytics SDKs may collect screen URLs
- Misconfigured universal links may open in the wrong place
Mobile apps should avoid logging sensitive URLs and should validate reset tokens only on the backend.
Practical Password Reset Security Checklist
Here is a simple checklist developers can use:
- Generate strong random tokens
- Keep tokens short-lived
- Make tokens single-use
- Store only hashed tokens
- Invalidate old tokens when a new reset link is created
- Avoid logging reset URLs
- Avoid third-party scripts on reset pages
- Use strict Referrer-Policy headers
- Apply backend rate limiting
- Prevent user enumeration
- Validate redirect URLs
- Prevent Host header poisoning
- Protect mobile deep links
- Invalidate active sessions after reset
- Send security notifications after password change
- Monitor suspicious reset activity
What Mature Security Teams Usually Do
Mature security teams treat password reset as a critical authentication flow.
They usually implement:
- Short-lived tokens, often 5 to 15 minutes for sensitive systems
- One-time token usage
- Hashed token storage
- Backend rate limiting
- Device and IP anomaly checks
- MFA or step-up verification for sensitive accounts
- Safe logging without raw tokens
- Session invalidation after reset
- Security notifications
- Monitoring for unusual reset behavior
The goal is not only to create a strong token.
The goal is to build a secure recovery flow around that token.
Final Thoughts
Password reset is not just a convenience feature.
It is a critical part of authentication security.
If the recovery flow is weak, attackers may not need to break the login system. They can simply abuse the password reset process.
The biggest mistake is relying only on HTTPS and assuming the reset link is safe.
A secure password reset system needs:
- Short-lived tokens
- One-time usage
- Safe token storage
- Safe logging
- Rate limiting
- Protection from Referer leakage
- Session invalidation
- Monitoring and abuse detection
A weak password reset flow can silently become the easiest path to account takeover.

Top comments (0)