| « Understanding the OSGI logging service | Old tricks never die... » |
The password recovery procedure is a major element of security for any web site that contains a login form or any form of authentication. All too often, however, it is left as a minor point of the development stage, and rarely gets a specification of its own, which leads to several security issues, even on major sites.
A good password recovery procedure has to be at the optimal level of compromise between security and usability. When the procedure is too easy to bypass, the security is compromised. When it's too complicated, it unnecessarily annoys the users, who may possibly give up at some point.
This simple rule of best trade-off between security and usability applies to most web sites, but not to some of them where the security needs to be psychologically enhanced. For instance, some categories of web sites (mainly online banks) define an overzealous security strategy, because they need to not only provide security, but also a strong feeling of security to the end-users. Most of the measures dedicated to the creation or recovery of accounts, such as not using email but only postal mail to send any information, sending the login and password in two separate letters, using one-time password generator devices, or just clicking on a random grid to write the password code, are more trouble and additional cost than real security, but it's nevertheless important to create a psychological evidence that the web site is secure. As for a lot of other things, it's not enough to have a secure web site: if the users do not feel the security is at the level they expect it to be, then the procedure may be technically secure, but wrong from a marketing point of view.
I'll detail some techniques commonly used, and finally provide a sample password recovery workflow, suitable for most web sites.
"Click here to receive a new password": this is very very wrong. Resetting a password to something random is not inherently incorrect, but doing it because _someone_ from the internet clicked on a button is not a good pattern. This may result in a denial of service for your users, simply because someone thinks it's funny to request a new password for them (and I'm not even mentionning funny things to do with a script requesting a new password every minute or every day).
The screenshots below are taken from a real site.

"Click here to receive an email that contains a link to reset your password": The user receives, in a mail, not the password but a secure link (https) to a web page where the password can be reset. This is a good trade-off between ease of use and security. The only drawback is that the user receives a weird email every time someone clicks on the button, so you may define some additional restrictions on the action. For instance, only 3 mails per hour, or answer a personal question. Otherwise, it's a strategy I suggest to use in most cases.
"Answer a few question about you": this one is pretty common.
For instance, the Yahoo procedure is the following: ask the login id, a captcha, some birthday information, the country you live in, a postal code, and the middlename or birth date. Wow, that's a lot of questions, so that's secure, right? WRONG. Any of your family members can answer it, and probably most of your friends, former friends, employers, and former employers. That's a crowd! And moreover, thinking about it a little further, imagine that you want to hack the account of someone, how long do you think it would take to collect this information from a co-worker, from a friend, from someone you meet at a bar ? Not more than 5 minutes, on average. Even less after a few drinks.

Note that any person asking me my favourite author will have an immediate answer, that's not something I'm willing to hide, specially if I've forgotten that this is the secret answer I set up a few months ago for my email account... OMG! Now it's published on a blog! I'm so screwed!
"Answer a secret question": a variant from the one above. For instance, Google proposes some "security question": "what is your library card number?", "what was your first phone number?", "what was your first teacher's name", etc, which are all much better than yahoo's (the information required are harder to social-engineer) EXCEPT from people that either can get a physical access to your stuff, or people who actually GOT this information, for instance anyone that was in your circle of friends when you had your first phone number (am I the only one that finds it weird that a security question is based on an information that you actually spent a lot of time spreading around?). So the "security question" should actually be rephrased "security question to prevent people from accessing your account, except when they know you, even remotely". To be honest, I don't really think the library card question is that bad, because it still requires a physical access to a personal object. However, any of my co-workers can easely access my wallet, I don't take it with me when I go to lunch or when a biological urge takes over. Of course, I trust my coworkers, but a good security procedure shall NOT be based on my trust and beliefs, it shall also take into account (at least partially) the fact that I may be social-engineered.
But the worse is still to come, it's the "write your own question". Have you ever had access to a database storing all the "own questions and answers" in plain text ? I did. When you let people choose their questions, not all of them, but a significant number of them, choose something trivial, based on a pun, on a date as complicated as the birth year of their child, or the name of their dog. This is the worst, because you cannot guarantee that the question has any level of security. And yet, you are responsible for the security, because you designed it the way it is.
Anyway, the Google's procedure is not that bad overall, because they actually use the secret question only after 5 days of inactivity on the account, which limits the possibility of hacking to the time periods where people can't access their mail (which means, for most people, during their holidays, when they are at the hospital, on a long trip, or anything else you can easely imagine). Just never leave your google account more than 5 days, and you're secure. The rule itself is a little weird, because it assumes that people are unlikely to be 5 days straight without checking their email, which may be totally correct in the silicon valley, but hardly applies to non-geek people.
So, if the simple rule to apply is "always find the right level of security", how should the typical password recovery workflow be designed ? It depends, of course.
The very first element you have to figure out is the secure perimeter that you are going to use in your user's environement. This secure perimeter defines how you are going to send recovery information, because you trust it against usurpation and disclosure. You don't need to trust it absolutly, just to trust it enough.
For most banks (in Europe at least), the only acceptable perimeter is the physical home of their customers, that's why they generally send the customer information there. Of course, this is not totally secure either, because people can still have their letters stolen, but absolute security does not exist in the real world, and this is the compromise that was defined in their security strategy. For phone operators, it can also be a mobile phone number to which an SMS (Text Message) is sent, it really depends on the data available. For an intranet in a corporate or academic environement, the secure perimeter may be the web administrator office, from whom you personally get a new password (in this case, the procedure often includes 10 minutes of ranting).
For most online sites, the secure perimeter is the email account. Some may argue, but the fact is, it's an acceptable compromise as long as you can admit that if someone can illegally access the user's email account, accessing your web site looks like a minor issue. However, using email as a mean to communicate with the user does not imply sending the recovery information in plain text (the email account may be compromised anytime), that's why it's much better to send a link to a secure password recovery page, rather than actually sending the password itself.
For the most standard cases (of internet web sites), I suggest the following:
Make sure the link is disabled server-side at some point (for instance, using a hashed parameted in the URL, and disable it accordingly in the database after the password was changed, and after a few days in any case).
So, here is the workflow, graphically (click to enlarge):
(the schema was done with balsamiq, a great tool).