A lot of websites take either of two annoying approaches to the problem of resetting your credentials when you've forgotten your password. Worse than that, one of these approaches potentially allows anybody to see if you've got an account, which could be undesirable for all manner of reasons and doesn't really adhere to the Privacy by Design requirement of the GDPR legislation1, which comes into force on 25th May 2018.
In this article I suggest a third, secure approach, which I reckon all websites should adopt. Importantly, I explain why this should also be extended to the account registration process, too.
To combat the problem of people losing (or forgetting2) their login credentials, most online accounts offer some kind of Forgotten Password functionality. Unfortunately, the two most common ways of achieving this are quite seriously flawed.
Don't tell the person you might have sent them an email
Some sites say something like "If you've got an account, we've sent you an email with reset instructions", which is really annoying because, when it doesn't turn up, you have no way of knowing if the email has failed to come through for one or more of multitudinous reasons, or if you also forgotten which email address you used to sign up and so have entered the wrong email address.
Don't tell valid users that you've sent them an email and invalid users that you haven't
Some sites say something along the lines of "Thanks, we've sent a password reset email" or "Account not found", which means anybody can find out if you have an account just by entering your email address and then noticing which message pops up on the screen. This could be really embarrassing or potentially dangerous (depending on the website).
There is a better way. And now we've seen what we shouldn't do, there's really only one logical course of action left.
Always send an email
In my opinion, the only sensible thing to do is always to send the person an email. Consider the following two scenarios:
- The user has an account.
- The user does not have an account.
If the user has an account, then send them reset instructions, with a reset link. Don't reset their password first and then send it to them, because (a) you should never know their password; and (b) a malicious person could reset their password, which would mean they'd be unable to login until they checked their email.
If the user does not have an account, simply send them an email saying something like "Sorry, we were unable to find an account for this email address" and perhaps offer them a way to contact a support team for further help.
In both cases, after the user has entered their email address and requested a password reset, you should show a message like "Please check your email for further instructions" and, importantly, it should always be the same message.
Ah, but what if...?
At this point you might be thinking that a smart attacker might notice that some email addresses show the "Please check your email for further instructions" message more quickly than others and might reasonably infer that this time difference corresponded in some way to real and invalid accounts. There are various ways to get around this problem, with the simplest perhaps being to run the account lookup and email generation on a separate thread. In other words, show them the message immediately and send the email later. This is somewhat suboptimal, of course, because the email might fail to send for some reason. However, the user knows that they are definitely meant to receive an email so, if it doesn't arrive, they know something has definitely gone wrong and they can try again (or contact support, although you should have some logging in place which would already have alerted you if the email was never even sent in the first place).
I'm sure we could think of lots of other ways of getting around this problem, but my advice would be to take the simple approach unless there's evidence that it's not adequate. I actually quite like the show-the-email-confirmation-page-immediately-and-run-the-lookup-and-send-email-on-a-separate-thread approach myself.
IMPORTANT: Extend this to account registration, too
Some sites have already thought of the above measures, but haven't protected their account registration page. This means that an attacker could simply try to register an account using your email address and, if they received an on-screen message saying that email address is already registered, they'd know you already have an account.
The way I have got round this issue when I've written such functionality is much the same as the forgotten password functionality described above. I let the user choose their credentials and then, when they click Register, I tell them to check their email.
Consider the following account registration scenarios:
- There is already an account associated with that email address.
- There is not already an account associated with that email address.
If the user already has an account, simply send them an email saying something like:
"You've already registered for an account with x. Please go to [login link] and try logging in (or resetting your password). If you need further help, email admin@..."
If they don't have an account, send them the standard new account email (which should probably have some kind of account activation link in it, in an attempt to verify that they're a real person).
In the interests of privacy you should never reveal to all and sundry whether or not somebody has an account on your website. The best way to achieve this is to make sure that your account registration and password reset functionality always shows the same response on the screen and that this response is a simple message telling the user to check their email for further instructions (and, of course, that you always do send an email to the address they entered). It's the content of the email that varies, not the message you show on the screen.
I wish all websites did this but, unfortunately, it seems to be quite rare, hence my not bothering to name and shame those websites which don't adhere to this advice (it would take too long).