I have a website which users register an account. On the registration field, the form fields are:
Name
Email
Confirm email
Username
But there is no password field. When they hit submit, they are emailed a password which is very complex, such as LHJVjhwnv%uu5 or RbWM9!jeDZUQb.
I have appealed to my developer to instead make it so that the users can set their own password on the registration form. To confirm that password on the form, and then be sent a confirmation link to their specified email. Then this way they can at least log in to their account and verify their email via confirmation link. Or if they didn't, every time they log into the site it could remind them to verify their email etc or else they can't do much on the site (example). This way even if they don't get the confirmation link, they can still update their account email to a different email and have it resent. At least at this stage, they can log into their account, instead of not at all.
The response I have received from the developer is as follows
"The problem with providing the password on registration is that
you'll have loads of fake accounts. So people that just register with
a non-existent email address. At least with the email validation
you're proving the user exists, to a certain extent. If they register with the wrong email, they can just re-register."
I'd like to ask you all if the current approach the developer has employed is acceptable?
If not, what are some good reasons I could use to convince the developer to change?"
I've tried to explain the following
Every day there are 9-10 people who register and then directly use the "password reset" form right after. This form involves them putting in their email address that they signed up with, and then it emails them a link to SET a new password. So if they are setting a new password anyway, why not just have them set it in the first place on the registration form? Why would there be 9-10 people every single day using the password reset field, directly after registration? I'm pretty sure its because they are seemingly struggling with these complex passwords (which I am not against) that are being emailed to them and are missing a key or character, or do not seem to be aware of copy/paste or something like that. If they could just set their own password the first time around, they wouldn't need to run to the password reset field right after because of their emailed password not working. I thought it was weird how everyday theres always password reset emails. Not for everyone, but a good 9-10 people a day ever since I started using Mandrillapp to track the outgoing emails. This is backed up by the next point.
Every day there are at least 2-3 people who fill out the contact form indicating that the password they received is not working. Could all be avoided if they just could set it on their own. There may be even more that just don't bother contacting.
Out of nearly 8000 accounts, 50% have never logged in. My strong suspicion is that the Registration email containing their password goes to their junk folder/spam folder. This is despite me having proper SPF, DKIM, etc setup. 2 months ago, I decided to start using Mandrill to send mail to ensure it goes to the inbox, but still there's at least 1-2 people/day that say that they did not get their email.. which perplexes me. If they could define their own password, they wouldn't have to worry about waiting for their password via email, or not getting it entirely. This just further highlights my initial concern.
Thank you for your time!
I assume you already have a working password-reset page in place, so you can use just the same code to do the registration:
Let the user register with his email.
Send a link with a token to the user, a hash of the token is stored in the database.
If the user clicks the link and if the token is valid, welcome him and let him enter his own password.
This is exactly what you already have for password resetting, your site becomes no more (un)secure because an attacker could as well use the password reset function.
Of course weak passwords are a problem, so rejecting weak passwords is a must. Again this is the same you would have to do on the password-reset page. For password-resets you wouldn't send plaintext passwords neither.
Unconfirmed accounts can be deleted after a certain period, it means the user didn't do anything with it. If necessary the user can register again. This only works if the user is not allowed to login before doing the confirmation, what supports the procedure above. Of course it depends on the website if this confirmation is that important though.
The main reason not to send passwords in a mail is security: Mail isn't a secure medium. If you put a password in there, then many people can see it - consider allowing people to use your service without any kind of password instead to understand what I mean.
The main drawback of allowing people to chose their own password is that 95% will use 123456 or password.
Most big sites use this approach to balance security, business needs and comfort:
Allow user to set their own password. Reject the most simple ones (there are libraries for this). Note: Complex password rules drive customers away.
Send a confirmation link via mail.
Don't allow login until the link has been clicked.
If they try to login anyway, tell them that they have to click the link first plus provide a "send confirmation link again" link in case the first mail was lost.
I have appealed to my developer to instead make it so that the users can set their own password on the registration form....
This opens you up to a plethora of weak password and reuse attacks. Reuse has been empirically studied and reported between 50% and 76%. Other academic studies have stated nearly all users (almost 100%) reuse passwords.
So assigning passwords helps with major practical problems. After you assign the password, then you are subject to the same attack, though because a user may reuse the assigned password on another site.
...
I'd like to ask you all if the current approach the developer has employed is acceptable?
Yes, its acceptable as long as the password meets security requirements. I even prefer it, especially when user selected passwords are not checked against bad password lists.
Bad password lists are important because we know attackers use them. There are literally millions of passwords on them because of all the past breaches (did I mention password reuse is a pandemic problem?). The lists are easy to find, and one researcher recently released his list of 6,000,000+. See How I Collect Passwords.
Aaron is correct about email and plain text. You can actually place another security control to limit the exposure. Namely, require that your mail server use SSL/TLS or fail the connection.
Or, generate the password and display it to the user on the screen. Instruct the user to print the password so they don't forget it. I even tell my users to write them down and keep them in their wallet or purse.
The primary threat is a network based attacker attempting to gain unauthorized access to the server through weak/wounded/bad passwords. So an out-of-band/out-of-channel email is not too great a concern. And a lost wallet or purse is not too great a concern. The concern is the network based attacker guessing passwords or using {username,passwords} tuples from breaches of other sites.
Every day there are 9-10 people who register and then directly use the "password reset" form right after. This form involves them putting in their email address that they signed up with, and then it emails them a link to SET a new password.
OK, that's a security defect since the user can circumvent the security control.
For what its worth, I regularly use a random password generator to generate passwords for accounts. I don't even bother writing them down. When I need to access the account, I simply go through the Lost Password process.
That's how low value these things are to me. Every damn site wants a user to manage an account and password. I'm tired of expending the energy to manage all the accounts, so I don't even waste the cycles on it.
The problem with providing the password on registration is that you'll have loads of fake accounts. So people that just register with a non-existent email address. At least with the email validation you're proving the user exists, to a certain extent. If they register with the wrong email, they can just re-register
OK, so this is a different problem. BOT registrations are usually mitigated with a CAPTCHA. I've never really studied what to use for Real Users/Fake Accounts.
What is the benefit of a fake account? It seems to me the user is trying to get half pregnant. They either have an account, or they don't have an account.
Out of nearly 8000 accounts, 50% have never logged in. My strong suspicion is that the Registration email containing their password goes to their junk folder/spam folder. This is despite me having proper SPF, DKIM, etc setup.
Well, that's a different problem (that you should confirm through testing). This speaks to displaying the password for the user and telling them print the page.
There's a lot more to the subject of password management. You should read Chapter 7 of Peter Gutmann's Engineering Security.
I have appealed to my developer...
Finally, you are the boss. Fire the developer or contractor if they are not following instructions or coding against requirements. I'd be damned before I begged someone who I was paying for services.
Programmers are a dime a dozen, and you can find plenty of them at places like Rent-A-Coder. The Indians and Pakistanis work for next to nothing (under $10 USD per day in some cases). I don't know how they can work for so little and survive...
Related
Users forget passwords, and (nearly) all membership sites need a way to help users get back in.
I'd like to implement the common scenario:
User hits site, tries to log in, can't, and realizes they forgot password - crap!
User enters email address and clicks "forgot password"
User gets email with a password reset link
Here's how I'm planning to implement this (C#/ASP.NET MVC):
When the user enters email and hits "forgot password" button my site will generate a GUID, store it on the member's entity in the DB (member.ResetToken), and email them a link with that GUID in the url (the email sent will inform them they can use this link to one time only)
User clicks the link and my site looks up their account based on that member.ResetToken from the url. If their account is found show them a password reset form, and when they complete the reset it clears the member.ResetToken from their account.
Here's my question: keep it like this (in which they can reset their password with that link at any time, now or in the future) or add a timestamp to limit how long they have to reset their password?
From a UX perspective the ability to reset your password whenever you're ready is great, but I want to make sure I'm not overlooking some security issues this could raise.
Your scheme actually works, but there are some points that could be improved. But first to your original question about the time limit:
Let's ask the opposite question: Why should a token remain valid infinit?
There is no advantage, when the reset-link can be clicked two years later, either the user clicks the link in about an hour or he has probably forgotten about the link (and can request a new one if necessary). On the other hand, being able to read the e-mails doesn't necessarily mean, that an attacker must hack the e-mail account, there is for example the open e-mail client in the office, a lost mobile phone, a backup on the (lost) USB drive...
The most important improvement is, that you should only store a hash of the token in your database. Somebody with access to the database (SQL-injection), could otherwise demand a password reset for any e-mail address he likes, and because he can see the new token, he could use it to set his own password.
Then i would store those reset informations in a separate table. There you can store the userid, the hashed token, an expiry date and the information whether the link was already used. The user is not in a special state then.
Maybe i misunderstood this point, but the reset link should point to a special page for password resets. When the user goes to the login page, there should be no special handling, the login page should not be aware that there is a pending password-reset.
The reset token should be unpredictable, this can be achieved best with a really random code, reading from the random source of the operating system.
So, there are a few problems with this approach that I was trying to elude to in my comment. When you store the "confirmation token" in the users password, you have just basically destroyed their password.
I, a malicious person, can then take a big giant list of email addresses and a bot net and flood your server with password reset requests and lock out your users from their account. Sure, your users will get an email for the reset, but if I can reset passwords fast enough, there may be a backlog of emails (or, if you do it synchronously, i can likely DoS the entire application).
I, a normal user of your system, may attempt to reset my password, and can't figure out why I'm not getting the reset email because I don't even know about a spam folder (or it never arrived). Fortunately, I just remembered what the password was, but it doesn't work anymore since the password is now an opaque GUID, and I'm basically dead in the water until I can find the reset email.
Here's the process you should use.
Generate a password reset request which you look up using a GUID, and you could likely also secure this by hashing that value with some private data and passing that in the URL as well to avoid a rapid attack. You can also lock this request down by making it only valid for a certain amount of time.
Once someone follows that link with a valid token and any other parameters you specify, they can then change the password, at which point you can now safely change the users password.
Flag the password request as having been completed, or delete it. You could also track information like IP address or something similar if you are really concerned about who changed the password if you are really concerned about it.
Send the user an email confirming that they have changed their password.
Also, just in case this isn't happening already, make sure you are hashing and salting the users password. It doesn't sound like you were doing that when you were just replacing the password with a GUID, so just double checking.
Users also forget to reset passwords (things happen). Being paranoid about passwords, I'd suggest limiting the link lifetime to 24 hours. This should be more than enough. It doesn't solve the problem of malicious intercept but it is better than nothing.
I would make the following suggestions:
Require some piece of information before the user is allowed to click the forgot password button. For example, require an email address and date of birth.
Ideally your user interface should not provide any feedback that allows a hacker to determine if his reset request succeeded. You don't want them farming your page for email addresses or DOBs. However this is a usability tradeoff, so whether you do this depends on how much security you really need.
You might also considering requiring a captcha which makes brute force and application DoS attacks much more difficult.
Expire the one-time token as quickly as possible. A couple hours is enough in my opinion. You should never consider email to be private-- it isn't, unless you are using a secure email technology (e.g. PGP) on top of the base protocol (most people do not). The last thing you want is for a black market to open up where your GUIDs are bought and sold, which is exactly what could happen if they have infinite lifespan.
Do not use GUIDs. They are not cryptographically random and are guessable. I suggest you use a cryptographic random number generator and translate it into base64.
Most of the online sites on registration do send a link to activate the site and on any further correspondence with the end user they provide information about the site and also provide the login credentials with password in clear text (as given below)
Username - myname#gmail.com
Password - mysecretpassword
What would you do in such a case? From a usability perspective does it make sense to send the password information in clear text or should you just avoid sending this information. I was under the impression that most of the passwords are MD5 hashed before storing in the database and hence the service provider will not have any access to clear text passwords, is this a security violation?
It's a commonly-held fallacy that if you receive a password in plain-text it means they aren't stored securely - passwords like any other data can be stored using reversible encryption.
Having said that, it's pretty likely anyone that sends you a plaintext password does not have a clue about security and is probably storing them carelessly (unless the passwords are used as weak real-world identifiers, say as part of an in-store membership scheme, in which case they shouldn't be called passwords lest your customers get confused).
If you send a password plain-text you may as well assume that if it is linked to something important then it has been compromised. There are just too many weak points. You can also do a lot more unintentional damage.
The email could be intercepted giving someone else the password.
Someone could see them open the email on their screen (been at mates houses and had this happen to both of us so many times, and every time is a massive headache to go change all your passwords).
The email might be forwarded to other addresses which are not secure.
The email might bounce/encounter a server error and then you (perhaps your untrusted staff or outsourced helpdesk too?), and the email server's system admin will probably get copies of the original email.
Someone who obtains access to the user's emails through a cookie hijack or even just a briefly unattended open email account will now be able to see their password. Worse, their password is probably used elsewhere (or at least has a common stem, e.g. "password1", "password1$$" "passwordSuperSecure123") so you've now compromised more than just your own service. Worse still, it might be the password to the email account that's been hijacked and now they can steal this person's email account and thus identity for a much longer time than the expiry date on the cookie/session. (This has all happened to people I know).
Yes, this is definitely a security violation. Only a salted and hashed version of passwords should be stored.
It is common to have reset password functionality that sends either a temporary auto-generated password (which should be good for only one login) or a one-time reset link. This does mean your other accounts are only as secure as your email.
However, you should steer clear of any site that will email your actual password in clear text.
There are always trade-offs, and developers have to consider useability, the savvy-ness of the intended users, the secrecy and importance of the data, the frequency that the website will be used, and so on. Of course users don't want their privacy violated, but on the other hand "ordinary" web users may be turned off by having to remember a password, or even having to invent one in the first place (some websites simplify user registration by generating a random password and emailing it). Website developers have a responsibility to keep the users' best interests in mind when designing security.
My advice is that passwords should only be emailed in the clear when they are randomly generated. This avoids the following awkward scenario: a user registers with a password which they are already using for various other web services, and then receives a registration confirmation email containing the password they just entered. A lot of users may not be security-conscious enough to use unique passwords for every website, but they are security-conscious enough to recognize that "sensitive" passwords should not be sent around by email.
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.
Optimally:
Log me in through OpenID. Less coding complexity, less clicking, less typing, less time wasted.
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:
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.
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.
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.
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.
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:
Usually I just copy-paste the password from the mail anyway. Copy-pasting a password is something you never want the user to do.
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.
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!).
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).
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:
The user enters a mail address in the "Password reset" form.
The mail is validated (ie. is there a user with this mail address in the database?)
The new password is randomly generated and sent by mail, then salted/hashed and saved to the database.
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.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Forgot Password: what is the best method of implementing a forgot password function?
I'm programming a community website.
I want to build a "forgot my password" feature.
Looking around at different sites, I've found they employ one of three options:
send the user an email with a link to a unique, hidden URL that allows him to change his password (Gmail and Amazon)
send the user an email with a new, randomly generated password (Wordpress)
send the user his current password (www.teach12.com)
Option #3 seems the most convenient to the user but since I save passwords as an MD5 hash, I don't see how option #3 would be available to me since MD5 is irreversible. This also seems to be insecure option since it means that the website must be saving the password in clear text somewhere, and at the least the clear-text password is being sent over insecure e-mail to the user. Or am I missing something here?
So if I can't do option #1, option #2 seems to be the simplest to program since I just have to change the user's password and send it to him. Although this is somewhat insecure since you have to have a live password being communicated via insecure e-mail. However, this could also be misused by trouble-makers to pester users by typing in random e-mails and constantly changing passwords of various users.
Option #1 seems to be the most secure but requires a little extra programming to deal with a hidden URL that expires etc., but it seems to be what the big sites use.
What experience have you had using/programming these various options? Are there any options I've missed?
4) Crediting their bank account with two random amounts and ask them to enter those in.
5) Snail mail them some new password and ask them to enter it in.
6) Have them text or call some number and enter some value to a phone number with the mobile phone they registered on file.
7) Get out of the password management problem altogether by outsourcing it to OpenID providers like Stack Overflow, Facebook, blog engines, and others are starting to do.
Outside of those, use option #1 or #2 with the added feature that both expire in an hour.
I'm shocked at the upvotes on answers describing #1 and #2 as equivalent. They aren't at all. Sending the user a short term link to change their password is the most convenient, most commonly used, and most secure approach that doesn't involve an out of band interaction (mail, text msg, etc.). A few reasons:
Setting a temporary password via a forgot password link allows users to effectively change a user's password and lock a user out of their account if they know the user's login. With a link, the user simply knows someone is messing around and their access isn't impacted.
The password reset link is only valid for a short period, so there's a very small window for an attacker to strike. And even if they did, the user would know because the reset link would no longer work if the attacker intercepted the link and used it to change the password. If the new assigned password isn't changed by the user immediately, the attacker who intercepted the password can quietly impersonate the user indefinitely. So the big difference is, while a hacker can intercept the reset password link email, if they use the link to change the user's password, the user will know something is wrong because the link won't work and they'll generate another password reset request.
Easier to use - the user simply clicks a link in their email rather than typing a new random password you've generated.
And security questions often make a site less secure, not more - they're another attack vector and often the weakest link. I highly recommend reading The Web Application Hacker's Handbook for an excellent discussion on this topic.
Note that Option #2 also requires you to keep track of the old password and expire the new random password if it isn't used within, say 24 hours.
Otherwise I could annoy you by repeatedly issuing you a new random password -- if you are not near your email you might not know why you cannot log in with your normal password.
Also, please avoid requiring an "identification question". The answers to these questions are typically much easier to guess/lookup than real passwords -- so everybody can identify themselves as me. See the Sarah Palin story for a recent example of how insecure this is.
Options 1 and 2 as insecure as each other.
There. I said it. If the user's email account has been breached, there's no reasonable secure way to do things unless you collect more private data like their address, mother's maiden name - all of which can be guessed.
The best (albeit most annoying) version I have seen is where you need to remember a secret question and a secret answer. It means the user has to remember which question they asked, which, of course, can always be forgotten too!
If they forget the question and you're a "real" company, there's always the option of sending the user a token through the post, with instructions on how to reset all their security... It's very unlikely that a hacker will have access to their real life mail.
A skew on that would be to collect a telephone number when the user created the account. If that existed and they couldn't remember any of their details, you could set up some sort of automated calling system that told them how to reset their details.
And one thing to mention about #2: Don't let the process overwrite the current account password. If that happened anybody could say they forgot any account's password, triggering lots of unwanted password changes.
There's no real difference between the security of option 1 or 2. Option 1 is effectively the same as preloading the new password in the form.
In fact, with the prevalence of phishing attacks, one could argue that encouraging use of option 1 with long URLs could make people less alert about clicking on long mysterious URLs.
Read the OWASP top ten to make sure your method is compliant.
Here is the direct link.
Just a quick note on something not specifically in regards to your question. You mentioned you used MD5 to hash stored passwords. Regardless of whether you choose to use Options 1 or 2 (3 is going to be the least secure as, for obvious reasons), MD5 is a cracked hashing algorithm, and can actually make it fairly easy for hackers to gain access to accounts protected by MD5 hashing.
You can read more about the vulnerability it at the following URL: en.wikipedia.org/wiki/MD5
A better hashing solution would be something like SHA, which is still a stable and secure hashing algorithm. Combined with option #1 or #2, you should have a reasonably secure system in place to protect your users passwords, barring all but the most determined hackers.
Option #1 is probably the best. #3 is insecure (and I also suggest using something stronger than MD5, such as SHA1). Option #2 is not good because it allows any random person to lock you out of your account until you check your email, unless you use a security question. And security questions are often easier to crack than passwords.
Option #1 has a couple of major advantages over #2. If a random user types in my email address into the "I have forgotten my password" box, then my password will not be reset. Also, it is slightly more secure in that there is no permanent record of the site's password stored in your gmail inbox forever.
A critical missing piece here is that the link you provide in #1 should only work for one password reset and have a time limit
All these solutions mean that you are treating your email inbox as the "one ring" that rules them all. Most online services seem to be doing this now days anyway.
My preferred approach is to go with openid where possible. Password management is hell that no one seems to get quite right. It's easier to hand this problem to someone else.
Option 4: Require user to reset password by entering their account name AND email address. As long as you aren't disclosing real names or email addresses on the site (WHY would you in this day and age?) this is a reasonably secure and tamper-proof method. Send a link to a reset page, not the password itself.
Option 5: Use OpenID and pass the responsibility to a 3rd-party to worry about it.
Honestly though this is a lot more effort than most sites require. I for one LIKE receiving plaintext passwords by email because I store them in a "registrations" folder in my inbox. That way I can lookup passwords for sites when I forget them (which happens a lot!). If somebody is reading my email I have bigger problems to worry about than people using my twitter account (if I had one). Of course banks and corporations have stronger requirements but you didn't specify what your site is. That's the key to the best answer.
I agree with your comments about option #3 being insecure.
As for programming either #1 or #2, option #2 is easier to program but #1 isn't much harder and both are probably about as secure as each other.
Whichever option you choose, you can also consider making it more secure by including requests for personal information (that you obtain during registration) as part of the forgotten password process.
I've programmed systems where you have a username and to get a new password you have to enter both your username and your email address. You can get sent a reminder of your username but the main point is that someone probably won't be able to guess your username and your email but if you do it just on email, there's less secure.
Secret questions are an approach to the personal information part. I personally think they don't offer a lot of value as people tend to choose questions that many people will either know the answer to, be able to guess or be able to find out. It is better than nothing however so long as you use it in conjunction with an already relatively secure method.
Obviously the more of this you do, the more programming work it is.
The simplest method is:
Have a "remind me of my username" link (enter email). Don't tell the user if an email was sent or not because people can use that to find out if an email address is of a member. Always tell the user to check their inbox for the reminder email but only send it if someone is a member; and
Require both username and email to get sent a new one-time password. That password should only last an hour or so. When the user uses it, they should be forced to change their password immediately.
Either option 1 or 2 would be fine. As you said, option 3 is insecure as you would need to store the clear text password. You could probably get fancy and use a reversible encryption algorithm to store/retrieve the password, but with better alternatives available to you there's no reason to go down that road.
There is an additional option that you can use in combination with any of the options that you mention:
You can let the user write a reminder for their password, that you send to them as the first step when they have forgotten the password. If the reminder doesn't help the user, you can go on to the next option.
As the reminder isn't the password itself, it's safe to send by mail (or perhaps even display directly on the page).
If you are hashing them Option 3 is unavailable and if you are not hashing them, shame on you. :)
I prefer option 1, sending a reset password link sent to their email which allows them (for a limited time) to reset their password. It does require more work, but it's easy for them to use and ultimately as secure as their email login process.
You could made a mix between #1 and #2, taking advantages from both:
Send the user an email with a link to a unique, hidden URL that allows him to change a new randomly generated password.
That page could be SSL, and the password could expire in 12-24 hours.
I've tried a couple of methods that I've not really been happy with. What I've settled on for the next project is to:
User enters username and email address
Email sent with link containing url and guid param which has been stored in db with 48 hour expiry
User confirms password to be reset
New password is emailed to user
Login with new password displays message or redirects to change password page.
Instruct the user come personally to your offices and prove her identity with id card or passport.
This, of course, assumes that you have offices near your users and that the account are valuable enough to justify this procedure. Suitable for example banks.
My company is developing an online HR and Payroll application where securing access is critical. I'm clear on how to lock down most of the authentication/authorization processes, except for the 'Forgotten Password' page.
My initial plan was to require the user to enter both an e-mail address and a response to a previously selected/entered challenge question, with a temporary password being mailed to the e-mail listed (assuming the e-mail is valid). But I've read here and here (both on SO) that the challenge-response approach is insecure.
If we're only e-mailing a temp password though, is it really that insecure? The only more secure option I can think of would be to require the user to call their Customer Service Rep, which would greatly burden our employees.
What am I missing ... is there a better approach? Thanks!
Don't email a temp password, email the user a URL+token to a reset-password page. That way no password is ever changing hands unencrypted. It's also immediately obvious to the end-user that their account has been compromised if they try to go to that page and the reset token has already been used.
Added from the comments:
I think challenge-response ("secret question") aspects actually make things less secure, because they are generally things that can be discovered by researching public info about the target. The fewer steps total, the fewer that can be broken without anyone knowing. Letting reset emails go early and often is a good way to let a human know the attempt is being made.
As explained in this article, Governor Palin e-mail account was recently hacked using answers to previously asked questions. From the article:
As detailed in the postings, the Palin hack didn't require any real skill. Instead, the hacker simply reset Palin's password using her birthdate, ZIP code and information about where she met her spouse -- the security question on her Yahoo account, which was answered (Wasilla High) by a simple Google search.
There are a few common ways to manage lost passwords:
The Secret Question: It is actually a weaker form of authentication, just like people above posted. User may choose something really simple and it is easy to guess. I advise against this, because it does not require any technical "hacking"
Mail a new password. To circumvent this control, access to the e-mail account is required or a Man-In-The-Middle (MITM) position is required: You either read the temporary password from user's inbox or intercept in the middle. This approach is ripe for misuse, because anybody can reset the password and force the user out of the system, if he can't read the e-mail with new password.
Mail a password reset hash, to circumvent this, you need access to inbox or MITM, just like in case before this, but no passwords are actually reset until confirmation is done. Thus, user can not be locked out of the system, even if he did not read the e-mail. Add a cooldown timer to one reset per 8 hours to prevent Your system from flooding user's inbox.
Consider some out of band communication, for example, in the printed contract, write down a PIN. Then have the user call Your helpdesk from a known phone number (check with Caller ID) and give his username and PIN.
Wouldn't it be easy/feasible to outsource the whole password management just like SO did and use OpenId or similar? Of course this would add another dependency, but you'd trade that against the need to save (and secure) passwords and deal with them as you described.
You said it is an on-line HR and payroll application. Do you have the option of a user indicating he/she has forgotten his/her password and that generating a message to an HR representative or some official in the organization who can confirm identity and then issue a password reset?
In short, challenge questions are often the weakest link. They're easier to guess than a password and effectively operate as a proxy for a password, so they actually reduce security rather than enhance it by providing another attack vector that's actually easier to break. The Web Application Hacker's Handbook has some great information on this area.