33

We have a system where if you forgot your password and want to reset it, to go to the forgot password page and enter your email address. A temporary link will be sent to your email to reset your password.

Now, when we subjected our app to penetration testing, an issue was found:

Application is giving clues of possible valid email addresses when attempting to reset password. This functionality can be abused by simply guessing possible email address and being able to find valid ones through the error messages.

Well, there's only one field and of course its obvious that if a reset password attempt fails, it's due to an invalid email. Seems this penetration test is wrong. Are there any solutions to fix this issue besides adding an additional field (besides email) for password reset?

4
  • 13
    it's important to clarify the difference between "invalid email" and "unregistered email" here. if the user types something that doesn't match the format of a valid email address, of course it's okay from a security perspective to inform them that the process failed. if they type a valid email that isn't associated with a registered user, however, that's information to be kept private. Mar 20, 2020 at 15:26
  • What response do you give a user that uses an incorrect email address at login?
    – DocRoot
    Mar 22, 2020 at 0:37
  • 1
    @DocRoot: It is a best practice to respond with a generic message such as "Invalid email or password" (assuming that you use email + password combination as credentials).
    – bhorkarg
    Mar 25, 2020 at 21:07
  • @bhorkarg Yes, I know. That was intended as a clue/rhetorical question for the OP. If they are providing a "generic" (error) message at a failed login attempt (as I would expect, since it's a very well recognised practise in interests of user-account security) then why provide a more "revealing" response at another stage in the user-account process?
    – DocRoot
    Mar 25, 2020 at 23:06

6 Answers 6

100

Don't indicate that the attempt "failed". A user (legitimate or otherwise), asks for a password reset link, and gives you an email address. All you should say here is along the lines of

Your submissions has been received. If we have an account matching your email address, you will receive an email with a link to reset your password.

The user will still get the link (success), but attackers won't know whether a provided email address is associated with an account.

17
  • 68
    Legitimate users sometimes forget which email address they used, so it's nice to give them a heads up that the request could fail due to a non-existent account vs. the email getting marked as spam, etc.
    – panofsteel
    Mar 19, 2020 at 22:59
  • 21
    Plus it just lets the user know what to expect. Yeah, most of us have been through the process before and know that we should look for an email. But for someone who is resetting a password for the first time, you want to let them know what to expect to happen next.
    – Elezar
    Mar 19, 2020 at 23:06
  • 41
    But the same thing can be found out via trying out registration. Unless you are saying "registration may or may not be successful" as well, which sounds terrible to me. All in all, it feels like easily bypassed obfuscation at the cost of usability (if a user mistypes his email, he will never find it out and wait in vain).
    – Alice
    Mar 20, 2020 at 3:46
  • 14
    Are these pages really necessary though? If I had to complete a survey, dismiss several ads and read some ToCs before being able to reset my password, I'd be seriously considering if I use that service at all. Resetting your password should be as simple as writing down my email address and clicking a button.
    – Christoph
    Mar 20, 2020 at 8:28
  • 16
    -1 Because you talk about the easiest implementation without even touching upon the related threat model. For example as pointed out in the comments exposing already registered user emails is a typical feature of registration forms, and the important thing is often to prevent brute forcing by rate limiting per IP. Nor do you touch upon the huge hit to UX and the dangers of giving the same response (real users being hurt more than malicious actors) Mar 20, 2020 at 8:38
30

The reason for this finding is that a user gets different responses, depending on the existence of the email address. This can be as simple as telling them that the address is not known, or something more subtle in the response.

The easiest implementation to avoid this kind of problem is giving back the exact same response, no matter whether the email address exists or not. A simple

The information to reset the password has been sent to the provided email address. If you do not receive an email, please check that you have supplied to correct address.

would do the trick.

If the email address is known to you, send the password reset information. If it doesn’t exist, don’t do anything. A real user will get the link, while an attacker cannot determine whether an email was sent or not.

6
  • makes total sense. thank you. why didnt I think of that before? Mar 19, 2020 at 13:43
  • 4
    @davshowhan449 - Don't worry, this is a very common vulnerability in web-applications and easily missed. Bad news is, that such issues are actually exploited in the wild and hard to mitigate on short notice, when it happens. Therefore, I recommend fixing it in time, although it is "just an information disclosure" on first glance.
    – Demento
    Mar 19, 2020 at 13:47
  • 23
    @davshowhan449 Then get rid of those additional pages, too. And good riddance, because that sounds like an awful experience in the first place. But maybe that is worth a new question.
    – Graipher
    Mar 20, 2020 at 9:49
  • 10
    @davshowhan449 I don't think I've ever seen that in a password reset page. You're presumed to already be a member of the service, there's no need to ask the user to agree to T&C. Other stuff doesn't show up until you respond to the link that's emailed.
    – Barmar
    Mar 20, 2020 at 15:26
  • 1
    @davshowhan449 Although it's still not a great user experience, one way to fix that workflow would be to move any necessary pages to after the user clicks the link in their email. Mar 20, 2020 at 21:39
23

What are we protecting against?

First of all one should ask what they are protecting against exactly. In this case there are two different threats:

  • Threat 1 An attacker brute forces random emails to find valid registered emails. This could be theoretically used to create spam lists, but as far as I know has never been done as it's simpler to just sent the emails than go through the extra trouble (The number of time that I have receive spam mail claiming my [some US bank] account needed action despite not living in the US is countless).
  • Threat 2 An attacker is targeting a specific user or a list of specific users. This is especially important when the bare fact someone is registered somewhere can have consequences. Example: The very act of having an account on Grindr or Ashley Madison can be dangerous in certain communities.

The bigger picture

The next question to think about is what other places might expose the same information and considering those as a whole. Typically the registration form will inform the user if the entered email is already registered, but this of course doesn't apply to most B2B software. Beyond that 'share with user' features (an input box where an email can be entered to share some object with this user) will often also expose this information, but as those are rare I won't include those in this answer.

Solutions for a system with public registration

First of all it's good to acknowledge to not informing the user that an account already exists during the registration process is from an UX perspective very unpleasant. The same applies to password reset forms that silently fail1 when the user makes a typo or has multiple emails. This doesn't mean it's something one shouldn't do, but one should weigh it against the security risks and benefits.

Given a system with public registration the important thing to consider is how big Threat 2 is to the users of your product. Given even a modest risk it can be prudent to change the registration form to start with only entering a name and email, giving a message "check your email to continue sign up", and if the email is already registered sending the user an email informing them of this. Similarly in that case the password reset form will not inform the user in any way whether the email is valid.

On the other hand if Threat 2 is minimal we have to model exclusively against Threat 1. Of course the previous approach will also function perfectly against Threat 1 exclusively, but considering the UX cost it's worth considering other solutions. The most obvious solution is rate limiting2 on both the 'email exists check' and 'password forgotten' checks (technically those calls can even go to the same API endpoint). These will often be designed to be fairly lenient for the first 10 calls or so, but get very limited very quickly after that point.

Important: Never remove error messages from the password registration without also implementing similar restrictions on the registration form.

Solutions for a system without public registration

It all boils down to the same issues as above (and I should have written this first), but without the 'extra' cost of the hit to the registration UX it's relatively 'cheaper' to have a secure password forgotten form, although of course it's still unpleasant for users and you still can consider whether rate limiting isn't enough for your specific threat model.

Notes

1: Instead of silently failing it's wise to at least sent an email to the entered email informing them that their email wasn't found. This 1) demotivates attackers from mass abusing the system (as it will be noticed) and 2) prevents users from wondering why they aren't getting an email.

2: Do note that rate limiting can negatively affect users of large networks or VPNs. Always consider how important that audience is to you and based on that spend an adequate amount of time to ensure the application stays functional even when rate limiting is harshest (e.g. by lowering the rate limit by solving a captcha or setting the maximum rate limit to around once per minute and ensuring the application will wait the full minute (note: still will be unpleasant given a team of users signing up for the same service at the start of the same meeting)).

5
  • 1
    Wouldn't it be also ok like this: "An e-mail has been sent to you for continuing signup, if you are already registered you will receive no e-mail." This would prevent an attacker to spam a for him known e-mail adress.
    – SAJW
    Mar 20, 2020 at 19:45
  • 1
    @SAJW I think what you're describing is the same as I described when I wrote "Given even a modest risk it can be prudent to change the registration form to start with only entering a name and email, giving a message "check your email to continue sign up"". But if you have any idea how to make it clearer or better: feel free to edit 👍 Mar 21, 2020 at 8:21
  • Ah, never mind. Your way is sufficient enough because if an attacker knows your e-mail, he can spam you if you are NOT registered, so we don't gain much with my solution.
    – SAJW
    Mar 21, 2020 at 20:37
  • @SAJW Somehow I completely misread your comment and now it's crystal clear... 😱😅. But yeah, in general silently failing is super dangerous, because the user never knows whether some slow mail server or spam filter caught his email, or whether he actually is supposed to not receive an email. Mar 21, 2020 at 20:44
  • This answer seems like the most complete. Just also remember time based attacks on your API/forms!
    – Jey DWork
    Dec 12, 2023 at 23:40
20

As an addition to the answers above (Would be better suited as a comment but can't do that yet) another step the hacker can take is to measure the TIME of your response to the form. It might take you 10ms to determine the email doesn't exists, while it takes you 100ms to generate a reset link and send an email. The hacker can know if the response is slower that its a successful find. You can employ a random sleep timer in cases where the email is not found so that both responses take the same amount of time.

7
  • 14
    Welcome to the site, have your first +1! Thanks for your addition. There is an even easier solution to the problem though. Just send the response right away, without checking the email first. That way the response time is independent from the validity of the email address as well.
    – Demento
    Mar 19, 2020 at 15:10
  • 8
    @Demento That's conceptually easy but may be technically difficult - a lot of basic web backends don't make such asynchronous processing easy, because by default they tie the lifetime of the processing thread to the HTTP response.
    – Bob
    Mar 20, 2020 at 0:19
  • 5
    @Bob yes it is a bit more difficult, but also still very tractable. If you have a message queue, you can use that, or go poor man / old skool with a cron that runs every minute popping items off a database of pending reset submissions.
    – Kroltan
    Mar 20, 2020 at 2:22
  • 3
    This problem goes away if you send an "unable to reset password: account not found" e-mail.
    – Brian
    Mar 20, 2020 at 13:11
  • 1
    Brian I upvoted you, but note that (without controls) you have given Attacker A the ability to spam user S using your service. Mar 20, 2020 at 13:37
9

You should weigh up the problem versus the inconvenience of solution. It's often a tradeoff.

In my opinion it is usually better to sacrifice this bit of security for the sake of user experience unless someone might get persecuted for being registered in your site.

From time to time I come years later to a site and try to find which email did I use for the particular site. The sites that hide this information are quite annoying.

4
  • 2
    A great addition to the other answers. Every security feature should be evaluated for the current context to decide if it's costs are worthwhile. The answer isn't always "yes". Mar 20, 2020 at 3:47
  • 1
    In the case where I don't know which email I used, I'll often search for the site name through my main email addresses to see which one I registered with. Especially useful inconjunction with the google feature of being able to add +anything to your email address.
    – stan
    Mar 20, 2020 at 6:50
  • 1
    It's not just about persecution, but also about phishing. Imagine someone gets a "password reset" mail and 5 Minutes later he received a mail apparently from your website saying "We recognized a login to/purchase from your account that might not be from you. please go to www.thisisascam.com to check whether your account was compromised". Lots of people will log in to the scam website, because the reset email primed them for it and the scammer knows that the person has an account with you, so he can format the message differently than in a generic spam mail.
    – user185186
    Mar 20, 2020 at 8:29
  • 1
    @Morfildur: Most phishers don't bother checking account existence before sending their e-mails. That's why you get phishing e-mails for services you don't use.
    – Brian
    Mar 20, 2020 at 13:13
1

Note that this also (besides the other answers) applies to the registration screen, instead of complaining about already registered user sent them an email that they are already registered and provide no feedback about this in the web app besides "check your mail".

This is best done if you ask for minimum input on the first step before the email validation (which is also good for GDPR, where you can prove that the user got your info email and continued to register).

In fact it's no hassle on registration and should always be done. For reset password function we provide an option in the software where you can select direct feedback or email feedback. (In corporate scenarios, especially on the Intranet guessing emails is not a real threat).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .