Some suggestions:
Don't reset the user's password until confirmed. Don't immediately reset the user's password. Only reset it once the user clicks on a confirmation link sent to their pre-registered email address.
Require a CAPTCHA. When a user requests that their password be reset, force them to solve a CAPTCHA before proceeding any further. This is to prevent automated tools from trying to give many users grief, and force the user to prove they are a human (not a robot).
Randomness. The time-limited password reset URL should include a random, unguessable component. Make sure you use crypto-quality randomness. The output from /dev/urandom
or System.Security.Cryptography.RNGCryptoServiceProvider
would be a good choice. The output from rand()
or random()
or System.Random
are not random enough and would be a bad choice. A GUID or timestamp is not random enough and would not be a good choice.
Include a time limit. The reset confirmation link should expire after some reasonable time: say, 24 hours. The link should be usable only once, and should immediately expire as soon as it is used.
Include explanatory text in the email. You may want to add some explanatory text to the email, to explain why the email was sent, in case someone requests a reset for an account that is not your own. You could include some text like "Someone has requested that the password be reset for your account username
on site
. If you made this request, click here to change your password. If you did not make this request, click here to cancel the request."
Send email after the password is reset. Once the password is successfully reset, send email to the user to let them know that the password has been changed. Don't include the new password in that email.
Monitor cancellations. You might consider including some logic to monitor the frequency with which users click the cancellation link indicating that they didn't request a reset. If this goes above a certain threshold, it might be useful to send an alert to the system operators. Also, if a cancellation link for some request is visited after the confirmation link is visited, that's a potential indication of an attack against that user -- you may want to take action at that point, e.g., invalidate the user's password and require them to reset their password again. (This is a defense against the following attack: the attacker gains access to the user's mailbox, then requests that their password on your site be reset, then visits the confirmation link. If the attacker doesn't delete these emails from the user's inbox, then when the real user reads their email, they may click the cancellation link, giving you an indication of possible trouble.)
Use HTTPS. The link should use https (not http:), to protect against various attacks (e.g., Firesheep attacks on users surfing the web from an Internet cafe).
Log these operations. I suggest logging all such requests. In addition to logging the user's username, you may want to log the IP address of the client that requested a reset link be emailed to the user, as well as the IP address of the client that visited the reset link.
Additional reading. You may also want to read Troy Hunt's excellent blog post, Everything you ever wanted to know about building a secure password reset feature. Thanks to @coryT for a link to this resource.
Lastly, consider non-password authentication. Passwords have many problems as an authentication mechanism, and you might consider other methods of authenticating users, such as storing a secure persistent cookie on their machine with an unguessable secret to authenticate them. This way, there is no password to forget and no way for the user to be phished, though you do need to provide a way for a user to authorize access from a new machine or a new browser (possibly via email to the user's pre-registered email address). This survey paper has an excellent survey of many fallback authentication methods and their strengths and weaknesses.