Sitemap

What Happens When You Click Forgot Password (Part 2)

6 min readNov 22, 2025

--

Press enter or click to view image in full size

Before we start looking into the vulnerabilities and best practices, let’s do a quick recap of what we’ve learned so far in Part 1.

We started by looking at password reset from your perspective as a user, clicking that “Forgot Password” link, entering your email, waiting for that reset link to show up in your inbox, and finally setting a new password.

From there, we checked out what was happening behind the scenes. When you submit your email, the server validates it and checks the database. It generates a random token (that long string of characters you see in the reset URL) and stores it with an expiration time. That token then gets embedded in an email sent to you. When you click the link, the server validates the token multiple times: Does it exist? Has it expired? Has it been used already? Only after passing all those checks can you set your new password, which gets hashed and stored while the token gets killed off.

Lastly, we talked about why this is important for security. Password reset isn’t just a convenience feature; it’s authentication through email ownership. You’re giving someone access to an account without checking their password. That’s why every validation step is critical.

Now that you understand how it’s supposed to work, let’s look at what happens when it doesn’t.

Common Vulnerabilities & Attacks

Alright, now we get to the fun part, which is seeing how things break. I spent tons of time reading about these vulnerabilities, and to be honest, a few of them are quite incredible.

Predictable Tokens

You know that story I mentioned about sequential numbers? That’s the worst-case scenario. If tokens follow any pattern: numbers in order, timestamps, anything predictable, you are basically handing attackers a guide.

Imagine this: tokens such as reset_12345, reset_12346, reset_12347. A person could create a program that attempts thousands of these each minute. Sooner or later, they'll find one and bam! They've gained access.

Press enter or click to view image in full size

Tokens that appear random might still cause issues if the type of randomness applied is unsuitable. Some programming functions that generate “random” numbers aren’t actually secure; they might repeat patterns or be easy to guess if someone knows enough about your system.

Tokens That Never Die

I have come across websites where password reset links remain valid for days or even weeks. That is a terrifying red flag.

Suppose you ask for a reset but delay using it. The link just sits in your email. If someone gets into your email account later, maybe you forgot to log out at a coffee shop, or your email got compromised, that old link still works. They can reset your password whenever they want.

Most sites expire tokens after 15 minutes to an hour. That’s enough time for normal use, but short enough that old links become useless pretty fast.

Using the Same Token Twice

Tokens that work multiple times. This one happens more often than it should

For example, say you have just reset your password successfully. That token should immediately get deleted or marked as used in the database. If that doesn’t happen, anyone who somehow got that token either from your browser history, server logs, wherever, can use it again later to lock you out of your own account.

Password Reset Poisoning

This attack is pretty clever, actually. The attacker manipulates the password reset request so that the link in the email directs to their server rather than the legitimate one.

They do this by manipulating something called the “Host” header when the reset request is sent. If the server just blindly trusts that header, it might build a link like:

https://evil-site.com/reset-password?token=abc123

You receive the email, it appears genuine, so you click it believing you’re updating your password…. You have just handed your token directly to the hacker.

Unlimited Reset Requests

Without any form of rate limiting, attackers can spam reset requests all day long. They could flood someone’s email (which is annoying), or they could blast through thousands of email addresses to figure out which ones are actually registered on your site.

That’s why most sites limit how many reset requests you can make. Maybe 3 to 5 per hour from the same IP address or for the same email.

Telling Too Much

Some sites try to be helpful and end up leaking information. They’ll show “Email not found” when you enter an address that doesn’t exist, or “Reset link sent!” when it does.

Appears harmless, doesn’t it? However, hackers exploit this to compile lists of accounts. They can methodically test email addresses and identify precisely who holds an account on your platform.

Get Nkiru Nwanolue’s stories in your inbox

Join Medium for free to get updates from this writer.

That’s why secure sites give you that intentionally vague message: “If an account exists, you’ll receive an email.” Because it doesn’t confirm anything either way.

Best Practices for Developers

Okay, enough scary stories. Let’s talk about how to actually build this properly.

Press enter or click to view image in full size

Generate Actual Random Tokens

Don’t just use any old random function. Most languages have specific cryptographic functions for this:

  • In PHP, use random_bytes()
  • Node.js has crypto.randomBytes()
  • Python offers secrets.token_urlsafe()

And make them long, at least 32 bytes, which turns into 64 characters when you convert it to text.

Expire Tokens Fast

The ideal timeframe ranges from 15 minutes to an hour. Users won’t get frustrated, but old tokens become worthless pretty quickly.

Press enter or click to view image in full size

Just store when the token expires in your database and check that timestamp every time someone tries to use it.

Kill Tokens After They’re Used

Once someone successfully resets their password, delete that token immediately. Or at least mark it as used. Don’t let it work twice, ever.

HTTPS Is Non-Negotiable

Those reset links have sensitive tokens in them. If you’re sending them over regular HTTP, anyone watching the network traffic can grab them. HTTPS encrypts everything, so the tokens stay protected.

Keep Email Status Private

Always use the same message whether the email exists in your system or not: “If an account exists with this email, you’ll receive a reset link.”

I know, it’s slightly less user-friendly. But it stops attackers from figuring out who has accounts on your site.

Throttle Reset Requests

Restrict the number of password reset requests an individual can make. For instance, allow 3 to 5 per email address within an hour and also limit requests originating from the same IP address. This prevents spam as well as brute force attempts.

Confirm Password Changes

After someone resets their password, send them another email: “Your password was just changed. If this wasn’t you, contact us ASAP.”

This method ensures that if any suspicious activity occurs, the real account owner knows about it immediately.

Consider Hashing Tokens in Your Database

This is extra paranoid, but some developers hash tokens before storing them, just like passwords. That way, even if your database gets compromised, the raw tokens aren’t just sitting there.

When someone uses a reset link, you hash the token from the URL and compare it to what’s in the database.

Conclusion

This sums up the whole process, from clicking “Forgot Password” to your new password sitting safely in the database.

What looks like a simple two-minute process is actually this whole carefully coordinated system working between your browser, the server, the database, and email.

Main things to remember:

  • Password reset is basically authentication through email
  • Tokens must be random, distinct, and temporary
  • You validate multiple times, so never assume a token’s still good
  • Sometimes being vague is better for security (like not confirming if emails exist)
  • Expiration times and rate limits are your safety nets

If you’re building this feature, take your time with it. Use proper random token generation, set expiration times, validate everything, and always use HTTPS. It’s not the most exciting code to write, but it’s important.

And if you’re checking existing code, now you know what red flags to look for. Check those tokens, verify the expiration logic, and ensure that rate limiting is implemented..

Next time you reset a password, you’ll know exactly what’s happening behind that form. And hopefully you’ll build systems that keep users safe when they inevitably forget their passwords.

--

--

Nkiru Nwanolue
Nkiru Nwanolue

Written by Nkiru Nwanolue

You found me. This is my digital diary where i document my journey in tech.