July 16, 2026
Password Reset Vulnerabilities List
By OWL
3 min read
Password reset functionality is one of the most sensitive features in any web application. If there's a mistake in how it's implemented, it can easily lead to account takeover. Whenever I'm testing a password reset flow, these are some of the things I always check.
— -
# Host Header Injection
- Direct Host Header Manipulation
The first thing I try is simply changing the Host header to a domain I control.
Host: attacker.com
If the application uses the Host header when generating the password reset link, the email might contain something like:
https://attacker.com/reset?token=xyzhttps://attacker.com/reset?token=xyzNow, if the victim clicks that link, the reset token gets sent to my server, allowing me to reset their password.
— -
2. Alternative Host Headers
Sometimes the application validates the Host header correctly, but a reverse proxy or load balancer trusts other headers instead.
Some headers worth trying are:
X-Forwarded-Host: attacker.com
X-Original-Host: attacker.com
X-Host: attacker.com
Forwarded: host=attacker.comX-Forwarded-Host: attacker.com
X-Original-Host: attacker.com
X-Host: attacker.com
Forwarded: host=attacker.comIf any of these are used to build the reset URL, you'll end up with the same impact as before: the reset link points to your domain instead of the legitimate one.
— -
3. Duplicate Host Headers
This one's less common nowadays, but it's still worth testing.
Some servers and proxies don't agree on which Host header they should trust.
Host: example.com
Host: attacker.comHost: example.com
Host: attacker.comIf one component reads the first header while another reads the second, weird things can happen, including generating a reset link pointing to your domain.
— -
4. Weak Host Validation
Some applications don't compare the host correctly. Instead of checking for an exact match, they use things like startsWith() or contains().
Examples to try:
Host: victim.com.attacker.com
Host: attacker-victim.com
Host: victim.com.attacker.orgHost: victim.com.attacker.com
Host: attacker-victim.com
Host: victim.com.attacker.orgIf the validation is weak, your domain might be accepted as a trusted host.
— -
# Weak or Predictable Reset Tokens
Not every reset token is random.
Some applications generate tokens using predictable values like:
-
Email address
-
User ID
-
Timestamp
-
Date
-
Base64 encoded data
For example, imagine the reset link looks like this:
https://example.com/reset?token=dmljdGltQGdtYWlsLmNvbSsyNS8wMy8yMDI2https://example.com/reset?token=dmljdGltQGdtYWlsLmNvbSsyNS8wMy8yMDI2If you decode it from Base64 using CyberChef, you get:
victim@gmail.com+25/03/2026victim@gmail.com+25/03/2026Now you know exactly how the application generates its reset tokens, making them predictable.
Just because a token is Base64 doesn't automatically mean it's vulnerable, but it's always worth decoding it and looking for patterns.
— -
# Reusable Reset Tokens
A password reset token should only work once.
Whenever I get a reset token, I always try using it multiple times.
I also test something like this:
-
Request a password reset.
-
Receive Token A.
-
Request another password reset.
-
Receive Token B.
-
Try using Token A again.
Ideally, requesting a new reset should invalidate the old token. If it doesn't, that's definitely worth reporting.
— -
# HTTP Parameter Pollution (HPP)
This one has shown up in real HackerOne reports.
Imagine the application normally sends the reset email using something like:
{
"email": "victim@gmail.com"
}{
"email": "victim@gmail.com"
}Now instead of sending a single email, try sending multiple values.
{
"email": [
"victim@gmail.com",
"attacker@gmail.com"
]
}{
"email": [
"victim@gmail.com",
"attacker@gmail.com"
]
}If the backend accepts both values, the password reset email could end up being sent to both the victim and the attacker.
It's also worth trying different parameter pollution techniques such as:
email=victim@gmail.com&email=attacker@gmail.com
email=victim@gmail.com,email=attacker@gmail.com
email=victim@gmail.com%20email=attacker@gmail.comemail=victim@gmail.com&email=attacker@gmail.com
email=victim@gmail.com,email=attacker@gmail.com
email=victim@gmail.com%20email=attacker@gmail.comDifferent frameworks parse duplicate parameters differently, so don't assume one payload is enough.
— -
# Account Enumeration
A good password reset flow shouldn't reveal whether an account exists.
For example:
❌
Email doesn't exist.Email doesn't exist.✅
If an account exists for this email, we've sent a password reset link.If an account exists for this email, we've sent a password reset link.If the application gives different responses for existing and non-existing users, attackers can enumerate valid accounts.
— -
# Rate Limiting
Can you spam password reset emails?
Try sending lots of reset requests in a short period of time.
If there's no rate limiting, an attacker could flood a victim's inbox or abuse the feature for denial-of-service.
— -
# Token Leakage
Sometimes the reset token is secure, but it gets leaked somewhere else.
Things worth checking include:
-
Referer headers
-
Browser history
-
Analytics services
-
Third-party scripts
-
Server logs
A secure token isn't very secure anymore if it ends up somewhere attackers can access.
— -
# Session Invalidation
After resetting a password, existing sessions should usually be invalidated.
If they're not, someone who already has a valid session can stay logged in even after the password has been changed.
This won't always be considered a vulnerability since some applications intentionally keep sessions alive, but it's definitely something worth testing.
— -
# Final Thoughts
Password reset functionality is one of those features where a single small mistake can turn into a full account takeover.
Don't just send a reset request and move on. Look at every part of the flow:
-
How is the reset link generated?
-
How is the token created?
-
Can the token be reused?
-
Does requesting a new token invalidate the old one?
-
Can you manipulate the email parameter?
-
Is account enumeration possible?
-
Is there proper rate limiting?
-
Are sessions invalidated after a successful reset?
A lot of these checks only take a few minutes, and you'd be surprised how many real-world bugs have come from them.