views:

124

answers:

3

When someone has lost a password, they click on the lost or forgotten password link. They will need to enter their email address, then answer their own secret question if the secret question is correct, an email will be sent to them with a link that expires after 24hrs.

As the email is sent, a record is entered in a database table holding this information: - the email of the person who needs to reset the password - the hour which the resetting of the password will expire - the hour which the request to rest the password was submitted.

The sent link will lead the user to a form that allows them to enter their new password. In this form they will need to enter their email address and their password X2.

When they click on submit, a check is made against db to ensure that the email is valid (one that the password is being reset) and has not yet expired (by comparing the two dates to see if the expiry time has passed, which is 24 hrs)

If the email is valid, and has not yet expired, and the two passwords match & meet the minimum req, then the new password is applied.

A confirmation message is given on success.

Q1. Is this a good model for password recovery? Q2. How can I make sure that the link that is sent to the user's address is unique? In that no one will get the same link? So that nobody could just go to the password reset page and try different emails, rather have each account that needs to be reset have their own unique URL that work for that account only.

Regarding Q2:

I was thinking that when the user requests to have their password reset, a random unique id is generated and stored in the same record that expires after 24hrs. This random unique id's column could be called "rid"

The link in the email that will be sent to the user will end with ?rid=xxxxxxxxxxxxx

When the user clicks on submit in the page that resets the password, the "rid" at the top of the page is used to get the corresponding email address from the db, to compare it with the email address in the form. Doing this can ensure that each password reset case will have its own unique URL that no other account can use to reset its password.

Is this a viable solution?

Any contributions or suggestions will be appreciated.

+1  A: 

Q1. IMHO, there is a flaw. Why are you asking the user to enter a new password? I would rather generate a new random password and send it to him. After receiving it, the user can login using this randomly generated password, then change it in his/her profile.

I suggest the following system, used on the most websites:

  1. The user enters a mail address in the "Password reset" form.
  2. The mail is validated (ie. is there a user with this mail address in the database?)
  3. The new password is randomly generated and sent by mail, then salted/hashed and saved to the database.
  4. The user logins.

It's easy and common, so the users will not be lost.

Now, you can increase security (decrease abuse) by:

  • Setting password reset ratio. Once the form is submitted, the IP address and submitted data is remembered: now, for a few minutes, nobody from the same IP address will be able to reset a password anymore.
  • A newly generated password does not replace the old one: instead, both can be used. Imagine somebody resets a password of another user. This user must still be able to login with the old password, if he/she has not read or received the mail with the new password.

By the way, but it's just my personal opinion, do not provide secret question/answer feature, unless it is required. I have too much pain remember what I answered where, so I would probably have more risk to forget the answer than the password.

MainMa
+1: I hate secret questions... first of all, I provide the answer and end up later posting it on facebook in my info box, etc. Stuff like "mother's maiden name" or "city where I grew up" is simply far too easy to research. Secondly, I rarely remember the answer I provided... Basically, I feel like it undercuts the security of actually having a secure password. Besides, you already know the email is legitimate (since that is what it was registered with) so why not just send an email link to reset, etc.
advs89
What flaw do you see? Sending a random password that is good for one login, or a random link that is good for one login have about the same security. The major advantage to the link is that the user can simply ignore false reset requests. If you send a random password, they have to take action (change the password back) for false requests. I also don't think having two passwords at once is a good idea. That will ease brute force attacks, only slightly, but still. As long as the user controls their email, they don't have to worry about false resets.
Matthew Flaschen
@Matthew Flaschen: the flaw is that the user is requested to enter a password. It's quite strange for me both as the user and as a developer. As a user, I would expect a new password sent to me. As a developer, I don't like the idea to have the same thing twice: would you have a password security check or submitted data control on three pages (register+profile+password change)? Generating a random password will not increase brute force risk if the password is long enough. And what's bad with false requests is not security concern, but that the user will be angry to receive too much mails.
MainMa
Sending the reset link is the one time use password. The only missing detail here is that you should alert the when they click on the link if the link has been used before, and the password reset (thus potentially someone has sent the link already). As long as the password has not been changed, and the link hasn't expired, then this isn't a problem. But if you follow the link and the password has been reset, you have a potential problem. Handling that is a bit of a pickle (as the user may have simply accidently clicked on the link later).
Will Hartung
Also, a person does not have total control over their email, and most email is transmitted in the clear across the internet. Wire sniffers, nosy administrators, people sharing a computer all are vectors to getting sensitive information out of email. Another thing I would suggest is nofifying the account holder every time a password has been changed. So, normal reset processing would have the user see the reset email, then then password change notification email. If they see the password change before they clicked on the link, there's issues. And send the email before the "owner" can change it.
Will Hartung
Well, I agree with you. In fact, it is better to send only an ID (an URL containing an ID for a reset) rather than a new password in plain text.
MainMa
Generating a new password is a bad idea. It may be that your password-generation strategy is flawed, etc. I do not think this is a good approach.
Noon Silk
Having a flawed password-generation strategy *is* a bad idea. ;)
MainMa
Nich
+5  A: 

Optimally:

  1. Log me in through OpenID. Less coding complexity, less clicking, less typing, less time wasted.
  2. Done. Question is moot. No need to worry any more, someone else already solved this problem.

There are a billion sites implementing a billion authentication schemes and in 99.999% of the cases it's unnecessary. Why should I as a user trust you as a developer to not store my password in plain-text or leak it or get hacked yourself? Few people use different passwords for every site...

If that's not possible, then make is as hassle-free as possible:

  1. I click a "forgot password?" link. This automatically sends a mail if I've entered an e-mail already (e.g. after a failed log-in attempt). If I haven't entered one, simply hide the password field and tell me to do so. Don't reload or forward me to a different page.
  2. I get a link to with some key (for example https://site.com/account/reset?key=a890ea8219175f890b7c123ee74a22). Some unique hash that is tied to my account and expires in so and so many hours. Use SSL if at all possible.
  3. I click the link and you fetch my account details by the key, making sure it is valid and hasn't expired. I type in my new password twice. I already know what my e-mail address is, and you already know what my e-mail address is, and any would-be hacker would also know what my e-mail was, so don't ask me to type it in. Having the link last a whole day is overkill. Time it out in an hour or less.
  4. I hate the security question. Don't ask me that. It's basically just a second password that's deliberately made less secure so you'll always remember it. I don't have frequent flier miles, I don't know my mother's maiden name, etc. Stop it. I know it's there to keep out strangers who might have hacked your e-mail, but unless you're PayPal I think it's just over-engineering.
  5. When you have confirmed that the passwords match and they are acceptable, update the database (store only a salted hash of my password, not the password itself!) and log me in immediately. Do not redirect me to the log-in page where I have to retype information I've already given you several times now (and even though you already know it's me and trust me enough to change my own password). That is so annoying. Users are impatient.

I also dislike randomly generated passwords based on the following:

  1. Usually I just copy-paste the password from the mail anyway. Copy-pasting a password is something you never want the user to do.
  2. I have to go change the password manually, which means messing around in the user control panel or account settings or whatever. I am getting impatient! You could pup up with a "change your password" the minute I log in with a one-time password, but this means added code complexity. You now need to add a flag for this, add another redirect, handle the case where a user times out on entering a new password, etc.
  3. Because I am only human, and humans have ADHD, I usually forget to do the above and I end up with a crap password I have to find and copy from my e-mail the next time I need to log in as well. My fault, but I'll still blame your site for it. ;-)

The randomly generated password solution is also implying the mentality that you'll always use a password-based solution, when link-based authentication seems to be the future (OpenID, etc.) in usability and security (I don't want a hundred small sites to know my login-details!).

Update to respond to comment:

Why the hash should be enough: if a hacker can guess the full 128+ bits (say) of your generated hash (and within an hour), then why wouldn't he or she also be able to guess the e-mail? I know that it "feels" a lot more secure to ask for the e-mail and/or a security question as well, but if you think about it, e-mails are usually very predictable and uniform, with low entropy rate. I doubt they can be counted on as containing more than 50 bits of information. (Probably a lot less.) I bet you I'd sooner guess your e-mail than I would a 50-bit random integer. But if it is a real worry, all you need to do is add more bits to the hash. 256 bits or so ought to do it for overkill mode -- SHA256(salt, email, old pw hash, time stamp, maybe some bytes from /dev/urandom) or similar... If a hacker can predict that, there's not much you can do really. Obviously he has control of The Matrix and could just project his mind onto the magnetic platters inside your hard drives if he wanted to.

Still Pro-OpenID: any new site I visit that demand I create a user ought to be very convincing as to why they want to know my (throwaway) password and what they offer me in security that OpenID or Google/Facebook/etc. does not -- or why they don't trust Google/Facebook/etc. Nobody (that I know) goes around remembering 30 different passwords. Usually people reuse them, so if these third party site creators wanted to, it'd be really easy for them to scam their users. If I registered at your site with my usual info, you could immediately take over my Last.FM and Reddit accounts if you wanted to, as well as probably a dozen sites I've used a couple of times and forgotten about. In fact, in these modern times, I kinda expect sites to either be ignorant or have malicious intent if they want details they strictly do not need, so that's why I call it a throwaway password -- with every sign-up it feels like I am saying "here, have my Reddit account, it's the same L/P as I'll be using for your site (otherwise I'd just forget). It's fine, I'm not particularly attached to it any way." Of course, Google could actually take over everything of my online self if they wanted to, but for now I'll trust Google more than you (no offense!).

integer
I disagree with OpenID solution. From users perspective, entering username and password and clicking "Submit" is much easier than be redirected to another website, search for the URL (WTF? why I can't just use my short username and must enter this long URL?), enter a password, click "Submit" and being redirected again.
MainMa
My only disagreement is on step 3. I should have to confirm I didn't just steal this URL or randomly find it (by typing my username/email). That eliminates the risk of a weak or predictable "key". And, +1 on OpenID, although that's also a form of "an email already on file" - it's just authenticated with a 3rd party instead of the user.
Jeff Meatball Yang
@MainMa: That's just a poor implementation of it. Try logging out from stackoverflow.com and logging back in. For me it's just a matter of clicking a single link and it automatically logs me in (since I'm already logged in with Google). That's how it should be done!
integer
@Yang: yes, but it's a trade-off where in virtually all cases I'll pick the site that doesn't require me to type my e-mail once again (I feel I need to type it in five times daily and I'm sick of it). I already trust the site not to leak your personal info / plain text password / e-mail, so trusting them to be able to competently generate a key is a shorter leap of faith (for me, at least).
integer
+1: I agree completely on the secret question. It _IS_ just a second password that is intentionally less secure. (which is stupid)
advs89
http://stackoverflow.com/questions/3191531/how-safe-is-openidI see that the security question needs to be removed. Why do I ask the email in the password reset form? Just in case the hacker guesses the unique reset url, the hacker would have to also guess the matching email. The data is hashed in the db and the form is in ssl.
Nich
I don't understand the link. I read it as a vague argument *for* OpenID? Either way, I updated the answer with some further arguments if you're interested and still unconvinced. I didn't find a way to message them directly.
integer
+1  A: 

First, I would suggest you treat a password-reset like a secure business transaction. SSL on, and a single request is independent of other requests for the same account. Generate a random, non-reusable transaction ID and associate it with THIS SINGLE REQUEST for a password-reset. Then, in the email you send to them, embed the transaction ID with the URL:

http://www.yoursite.com/passwordreset/?id=e3dXY81fr98c6v1

This so-called password-reset transaction should be associated with the account and have the metadata associated with a password-reset such as date of request and expiration date. The account itself should only know that there is/are pending password reset(s) associated with it.

Also, when a user asks to reset their password, forget the secret question - it's far more secure to send them an email that you should ALREADY have on file. (If you don't, then start collecting them!) - better yet, use their email address as their username.

When the user enters a new password from that transaction ID URL, you are trusting that it is the person you think it is. To ensure this, you can simply ask them to re-enter their username (which you already confirmed you are doing).

Jeff Meatball Yang
Yes, SSL is a good idea, I forgot to mention it.
integer
Nich
Yes. The email address on file is the one to receive the link.
Jeff Meatball Yang