29

We currently have what I believe to be a pretty standard scheme for dealing with password resets. Our reset links are single-use links: They expire immediately after they've been visited, even if the user doesn't actually reset their password.

However, our customers are predominately (99%) businesses with aggressive spam filtering. In particular, some of our biggest customers (school districts) have a spam filters in place that perform link-scanning. They visit [up to N] links in an email as part their algorithms. When users request a password reset, the links are "expired" by the spam filter's visit before the user sees them.

Are there any alternatives to the single-use link that are equally secure? Or that are secure enough to fall within the realm of acceptable practices?

We also need to consider usability. Our customers are generally about as non-technical as you can get. So ideally, the password reset procedure won't become [much] more complicated for the user.


Here's what we've thought of so far:

  • Store the reset token in the session. The link would remain active while the original browser session is open and/or the password hasn't actually been reset. It may complicate the process for users who use two different devices for their email and browsing (e.g., email on phone + laptop for browsing).
  • Expire the link after N minutes. I think I've seen this. But, I don't know what time limit is an acceptable balance between usable and secure.
  • Expire the link only after the form is submitted. Some users may visit the link, putting it into a browser history, but never submit the form. Is that an acceptable level of risk?
2
  • Comments are not for extended discussion; this conversation has been moved to chat.
    – Rory Alsop
    Commented Sep 2, 2014 at 14:07
  • So if I send an unsolicited mail like "Click [here] to acceptout terms an condition and then click [here] to buy 1000$ worth of snake-oil", the spam filter might make a binding contract? - Or it could simply be made part of a DDOS attack against websites? Commented Mar 13, 2015 at 18:21

6 Answers 6

18

As always, you have to consider the value of the asset you're attempting to protect in order to properly assess if a security procedure is adequate or not. Typically, you'll be willing to accept lower usability when the value of your asset is high.

This means that it's impossible for anyone than yourself to estimate whether a given solution is secure enough.

That being said, a simple way to solve your issue is not to have your links expire immediately but have them expire after a period of time OR when the password has been reset (whichever comes first).

In order to decide whether this is secure enough for you, and assuming you're satisfied current setup, you will need to consider the following changes:

The same link can now be reused several time. This potentially weaken the security of the system.

However, you are currently working on an assumption that is incorrect: the first person to visit a link is the legitimate user. It's clearly not the case (as you've noticed). The consequence is that you're not really increasing the overall security by having the link expire immediately after first access.

So, in my opinion, changing the way your access token expires seems like a better solution: you're increasing usability without impacting the overall system security.

You might, however, consider including some second-level validation in the page you land in when using your reset link. Something that will somehow improve your confidence the user identity (typically, a "security question" is used although that model is not very good).

4
  • @Chris I was attempting to differentiate between potential impact (before analysis) and actual impact (after analysis). Maybe you're right: I should rewrite in a more condensed form
    – Stephane
    Commented Aug 26, 2014 at 13:52
  • "consider the value of the asset you're attempting to protect" -- duly noted. You can't tell me when the link expires because you don't know the value of the assets. But, let's assume there's some sensitive data that we're obligated to protect. Malicious activity on the account could result in leakage of student data or loss of [low stakes] assessment data. Are there standard practices in place when it comes to student data (remember these are minors/little kids)? Or perhaps more generally, are there established rules that would help determine an acceptable link expiration period?
    – svidgen
    Commented Aug 26, 2014 at 15:28
  • You can't tell me when the link expires because you don't know the value of the assets No, I can't give you a meaningful estimate about how much usability (and cost) you should sacrifice to security.
    – Stephane
    Commented Aug 26, 2014 at 15:37
  • 2
    are there established rules that would help determine an acceptable link expiration period? Not really. Some links will expires within the hour, some after a few days. My personal opinion is that people request password reset when the want to use the password, not the day before so expiring it after one hours seems safe. However, be aware that some mail system might delay incoming mail for longer than that so it might cause issue in some isolated cases.
    – Stephane
    Commented Aug 26, 2014 at 15:40
8

I don't see the security of a single use password reset link like you describe it. Most single use links are only invalid if the password is changed, not if the page is loaded for the first time. An accidental press on reload or F5 will make the request invalid? This is some kind of a very bad user experience.

So let's think about this: A user orders a password link, two things can happen:

  1. A 'hacker' gets the mail first. By using your system the actual user has no chance to even open this link, this is very bad for the user and you. But in this case there is no additionally security by letting the link expire by first time usage.
  2. The user gets the mail as desired. Everything is Ok here, but if an accidentally refresh is made, the user has to start with ordering a new link.

To make the system more secure you could build a system where an additional mail is sent to the user with a link to 'recover' the old password, if he/she wasn't the one who changed it. I do not want you to send the old password in plaintext to the user (you shouldn't even be able to do so). What I mean is to historize the old password hash and salt and if the user clicks 'revert' you just use the old hash and password again. Surely, this won't help if the mailaccount is compromised, but for your example with a public computer where the reset link is stored it would help.

Additionally you could forbid to change the accounts mail the first X days after a password change. That way a Hacker could not lock out the user completely (as long as the mail account is not compromised, but in this case there is very little you can do).

Another option is to give the user a code after registering (per old-fashioned letter for best security, if this is too expensive show it on the page itself when the user logs in for the first time with the express request to print it out - still better than sending it to the possible compromised mail account) which acts like an emergency-recovery for the given account. For example when this code is entered, the user can enter a new mail and password to gain access to the account, no matter what happens. But this code has to be very secure, and should really in no way be saved on any computer.

Even more security would be added by using two-factor authentication, like you can use with Google, GitHub and many more. You can even use the system from Google, so you don't have to build everything yourself. This way an attacker has to steal the user's cellphone - making random attacks impossible.

And lastly, a very big and important point: NEVER send the password via mail. I've seen many big websites which do this and there is no way this could be a good idea!

5
  • 1
    _To make the system more secure you could build a system where an additional mail is sent to the user with a link to 'recover' the old password_ Nonono... You should never be able to recover an old password because the password should never be saved in a recoverable way in the first place.. Commented Aug 26, 2014 at 8:17
  • If a "hacker" gets email access, then all bets are off. This is a scenario you can't protect against.
    – Agent_L
    Commented Aug 26, 2014 at 8:34
  • @ChristianWattengård I agree with you, but I think you got me wrong: I don't want to resend the password to the user! All I meant is to enable the old password again, so the user could log in with it again (perhaps by storing the old pw-hash and salt in a history table)
    – Tokk
    Commented Aug 26, 2014 at 8:37
  • 1
    I think the bottom line is, if you want to be secure, 2FA is the way to go. Commented Aug 26, 2014 at 8:52
  • @Chris, yes, you need something else. Google (and Github I believe) use a limited set of single use static 2FA codes. I keep mine in my wallet. I think there is some other recovery procedure, but it's lengthy and involved. Commented Aug 26, 2014 at 18:08
2

The real threat is that an account could be hijacked. Currently your security is based on keeping password reset links secret, because with it anybody may set a new password at will. Using a single-use link, doesn't help because you don't know if the first visitor is the actual user or an imposter.

To improve your situation, there are three options

  • Keep the password reset link secret by using a secure medium instead of unencrypted emails. For example PGP-encrypted email, SMS (not encrypted but more difficult to intercept), TextSecure message, paper letter, ...
  • Don't rely on the link to be secret. Then you need some other way to authenticate the user. All examples I can imagine are costly: a more complex password which was sent by postal services (like a PUK), sending a fax with personal information (could also be faked, but is more difficult), additional questions which only the user could answer ...
  • Keep your current solution, allow multiple visits to the password reset page, reject attempts after a defined period of time (for example 2 hours) and hope that your network is secure. (Anecdote: Until 2013 most email providers in germany exchanged emails unencrypted.)
1

One approach to this problem, which I am considering implementing myself could work as follows:

  1. User opens the password reset page, which has a form with an email address field.
  2. User POST the form with their email address
  3. Server send a one-time password to the email address entered by the user.
  4. The server reply to the POST request with a new form containing:
    • Hidden field with a secret value
    • Text field for entering one-time password
    • Two password fields for entering the new password
  5. On submission of the second form the server verify that the secret value in the form and the one-time password being entered do in fact correspond to each other. Additionally it verifies that the values are no older than x hours (for some x between 1 and 24).
  6. If everything was entered correctly, the password is reset.

This is similar to using a session cookie, but the secret value in a form field is likely to be less persistent than a session cookie. It is possible to combine such that the first POST request create three values one in the email and two in cookie and form field in the POST reply, and the second POST request must contain all three values to reset the password.

A variation of this approach, which I am also considering is as follows:

  1. User opens the password reset page, which inform them about a one time email address on the same domain as the web page (or a subdomain). Additionally the page contains a form with a hidden field with a secret value and a one-time password field.
  2. User send an email to the address, which they were informed about.
  3. At the end of DATA, the receiving mail server immediately starts sending a reply to the user's email address. This reply will contain a one-time password.
  4. The one-time password that was send to the user is now copied from the email into the form.
  5. Server verifies that hidden field and one-time password correspond to each other (connected through the one time email address).
  6. If successful the user is presented with user accounts registered to the verified email address.

The second approach might not be completely thought through yet, but I imagine it has the following advantages over simply sending an email to the user:

  • You have one more opportunity to validate that the user does in fact control the email address, as you can validate SPF records at this point.
  • You are less likely to disturb users with illegitimate password reset emails, as an attacker would have to pass the SPF check before the reset mail will be sent.
  • The reset mail is less likely to be blocked by a spam filter, because it is an actual reply to an email sent by the user.
12
  • It's a potentially good solution if the form is encoded with a secure hash of the token and a server-side-key, which is what I'd assume you meant.
    – svidgen
    Commented Aug 26, 2014 at 15:31
  • @Chris Manipulating the cookie and/or hidden field on the client side is going to achieve nothing. It is trivial to construct those values such that the server can know if the values it get back were produced by the server itself. Changing the login password before the one-time password has been used, is not an option for me. So a separate flow for resetting the password is needed, at least in my use case.
    – kasperd
    Commented Aug 26, 2014 at 15:39
  • @Chris If I didn't use a one-time email address the way I describe, I would need some other way to connect the one-time password to an email address. I imagine the user will send the mail by clicking on a mailto link, in which case it really doesn't matter to the user, if the address changes each time.
    – kasperd
    Commented Aug 26, 2014 at 15:43
  • @Chris Getting the reset email is indeed the most difficult part for an attacker. This part gets a lot easier for the attacker, if the attacker simply need to get any old reset email, which did not get used after all. By tying the one-time password to a form rendered before the email was sent, it becomes a lot harder to use an old password reset email. The second most difficult for the attacker is to send an email from the address of the victim. Requiring both makes an attack a bit harder than just sending a mail to the user.
    – kasperd
    Commented Aug 26, 2014 at 15:51
  • 1
    @Chris Yeah. This answer doesn't explain it quite how it needs to work. Done correctly, there's still email validation that occurs. It's just a matter of how the email-code is validated. It was my understanding (assumption) that the emailed token can be kept out of any server-side permanent storage altogether by putting a hash on the page. It comes with the benefit of keeping tokens out of permanent storage and basically letting the user decide when the token expires (whenever they leave or close the page).
    – svidgen
    Commented Aug 26, 2014 at 16:15
0

Mail system you're dealing with has a huge security breach by itself. A robot reading secure mails is no problem, but a robot that sends parts of supposedly secure message (links) out to the internet is plain unacceptable. Sad as it is, it rules out links as secure means of transporting data from users to server. You need to consider other options, without customized links at all:

1) Replace links carrying data in URL with generic link to a form + copy-pastable security code. It's certainly worse user experience, however if you unclutter the mail and form to bare minimum and provide short and clear instructions, it should be manageable for everyone.

2) Use one-time passwords instead. The mail would contain one-time password and instructions to log in with it, which would redirect user to "define new password" page.

I recommend second approach, as it's less threatening to users (they are already familiar with login procedure and you can disguise the whole process to look similar to login), you don't need to maintain yet another page (the "enter reset code" page), and the message is short and compact ("your new password:") so you can use other means of delivery, like text messages.

6
  • Emails are not secure unless you encrypt them so there is no security issue here.
    – Stephane
    Commented Aug 26, 2014 at 9:44
  • @Stephane Certainly OP considers them secure enough to send password reset links!
    – Agent_L
    Commented Aug 26, 2014 at 13:01
  • The problem is with the password reset system, not the mail system as you imply
    – Stephane
    Commented Aug 26, 2014 at 13:22
  • @Agent_L Your first option might be an acceptable degradation in the user experience. Something to consider. Though, I'm not sure how relevant the preceding paragraph is to the suggestions ...
    – svidgen
    Commented Aug 26, 2014 at 13:46
  • @svidgen It's relevant because it gives you a constraint: all links in mail must be considered public, therefore visiting them can't create any side-effects.
    – Agent_L
    Commented Aug 26, 2014 at 13:58
0

You should consider using two methods simultaneously:

  1. Expire the link after N minutes/hours

  2. Expire the link only after the form is submitted.

This should be good compromise between security and usability.

You must log in to answer this question.

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