Best way for a 'forgot password' implementation? [closed] - security

As it currently stands, this question is not a good fit for our Q&A format. We expect answers to be supported by facts, references, or expertise, but this question will likely solicit debate, arguments, polling, or extended discussion. If you feel that this question can be improved and possibly reopened, visit the help center for guidance.
Closed 10 years ago.
I'm looking for the best method to implement a "forgot password" feature.
I come out with 2 ideas:
When user click on forgot password, the user is required to key in the username, email and maybe date of birth or last name. Then a mail with temporary password will be sent to the user email account. The user uses the temporary password to login and resets his password.
Similar, but the email would contain a link to let the user reset his password.
Or anyone can suggest me a better and secure way? I'm also thinking to send the temporary password or link, force the user to reset the password within 24 hour, or else the temporary password or link will not be usable. How to do that?

Update: revised in May 2013 for a better approach
The user enters his username and hits "forgot password". I also recommend the option of entering the email address instead of the username, because usernames are sometimes forgotten too.
The system has a table password_change_requests with the columns ID, Time and UserID. When the new user presses the button, a record is created in the table. The Time column contains the time when the user pressed the "Forgot Password" button. The ID is a string. A long random string is created (say, a GUID) and then hashed like a password (which is a separate topic in and of itself). This hash is then used as the 'ID' in the table.
The system sends an email to the user which contains a link in it. The link also contains the original ID string (before the hashing). The link will be something like this: http://www.mysite.com/forgotpassword.jsp?ID=01234567890ABCDEF. The forgotpassword.jsp page should be able to retrieve the ID parameter. Sorry, I don't know Java, so I can't be more specific.
When the user clicks the link in the email, he is moved to your page. The page retrieves the ID from the URL, hashes it again, and checks against the table. If such a record is there and is no more than, say, 24 hours old, the user is presented with the prompt to enter a new password.
The user enters a new password, hits OK and everyone lives happily ever after... until next time!

It all depends on your site and the level of security that you're trying to achieve but the basic process for a web app goes something like the following:
The user navigates to the 'forgot my password' page and enters their username or email (whichever is unique) to request a password reset.
Optionally at this stage you can confirm the request by asking for additional information such as the answer to a predefined security question or their date of birth etc. This extra level stops users receiving emails they didn't request.
Look up the user's account. Save a temporary password (usually a GUID) and timestamp against the account record. Send an email to the user containing the temporary password.
The user either clicks on the link containing the temporary password and the user's identifier in the email or navigates to the 'forgot my password' page and copy & pastes the temporary password and their identifier. The user enters their new password and confirms it.
Look up the user's record and if the current time is within a specified time limit (e.g. 1 hour) of the timestamp saved in step 2 then hash and save the new password. (Obviously only if the temporary passwords match!). Delete the temporary GUID and timestamp.
The principal here is that the user is emailed a temporary password that let's them change their password. The originally stored password (it should be hashed!) is never changed to a temporary password in case the user remembers it.
The original password will never be displayed to the user as it should be hashed and unknown.
Note this process relies entirely on the security of the user's email account. So it depends on the level of security your wish to achieve. This is usually enough for most sites/apps.

Troy Hunt makes some excellent points in his article, Everything you ever wanted to know about building a secure password reset feature. The most relevant excerpts are:
[T]here are two common approaches:
Generate a new password on the server and email it
Email a unique URL which will facilitate a reset process
Despite plenty of guidance to the contrary, the first point is really not where we want to be. The problem with doing this is that it means a persistent password – one you can go back with and use any time – has now been sent over an insecure channel and resides in your inbox.
...
But there’s one more big problem with the first approach in that it makes the malicious lockout of an account dead simple. If I know the email address of someone who owns an account at a website then I can lock them out of it whenever I please simply by resetting their password; it’s denial of service attack served up on a silver platter! This is why a reset is something that should only happen after successfully verifying the right of the requestor to do so.
When we talk about a reset URL, we’re talking about a website address which is unique to this specific instance of the reset process.
...
What we want to do is create a unique token which can be sent in an email as part of the reset URL then matched back to a record on the server alongside the user’s account thus confirming the email account owner is indeed the one attempting to reset the password. For example, the token may be “3ce7854015cd38c862cb9e14a1ae552b” and is stored in a table alongside the ID of the user performing the reset and the time at which the token was generated (more on that in a moment). When the email is sent out, it contains a URL such as “Reset/?id=3ce7854015cd38c862cb9e14a1ae552b” and when the user loads this, the page checks for the existence of the token and consequently confirms the identity of the user and allows the password to be changed.
...
The other thing we want to do with a reset URL is to time limit the token so that the reset process must be completed within a certain duration, say within an hour.
...
Finally, we want to ensure that this is a one-time process. Once the reset process is complete, the token should be deleted so that the reset URL is no longer functional. As with the previous point, this is to ensure an attacker has a very limited window in which they can abuse the reset URL. Plus of course the token is no longer required if the reset process has completed successfully.
He makes many more good points about avoiding information leaks, CAPTCHAs, two-factor authentication, and of course the basic best practices like password hashing. I think it's important to note that I disagree with Troy on the usefulness of security questions, preferring Bruce Schneier's skepticism of the practice:
The point of all these questions is the same: a backup password. If you forget your password, the secret question can verify your identity so you can choose another password or have the site e-mail your current password to you. It's a great idea from a customer service perspective -- a user is less likely to forget his first pet's name than some random password -- but terrible for security. The answer to the secret question is much easier to guess than a good password, and the information is much more public.

I'll go with:
Ask user for email, check email is registered
Generate GUID, and send it to that email
Do not reset password yet
User clicks link, and then have to enter new pass
Reset password only after user is in your site, and have clicked reset button after typing new pass.
Make that GUID expirable within a short time period to make it safer.

When you are sending any information via email, it won't be secure. There are too many ways someone can get it. It would be child's play for a skilled hacker looking to steal your information.
Refrain from sending any personal information like passwords and income information via email as it can become VERY EMBARRASSING for you and your organization if such information was leaked or stolen. Think about security seriously. It just takes that one incident for all the bricks to fall.
As for password retrieval, thoroughly read Forgot Password Best Practices.
The bottom line is that an application
following best practices should allow
a user to reset his own password.
Personal security questions should be
used. The application should not send
email, display passwords, nor set any
temporary passwords.
EDIT: Updated link

As said, it depends on the level of security required, however, if you need a higher level, some novel solutions I have seen include;
Displaying half of the temporary password when the user's identity has been confirmed (security question, email address etc.) then the other half being sent to the email account. If the email account has been compromised, it is unlikely that the same person has also managed to perform a man-in-the middle attack. (Seen on UK Goverment Gateway)
Confirming identity via email and another medium - for example a code sent via text to a registered mobile. (Seen on eBay / PayPal)
For somewhere in between these two extremes implementing security questions may be the way to go as mentioned by DaveG.

If you include an email address with the registration. The "forget password" button sends an email to that email address. It ensures that the information is send to a trusted email.
(Unless the database is hacked, but then nothing is safe).

I would enforce unique email addresses across the accounts.
Then it is a simple matter of sending a link to a temporary page that allows the person to change their password. (allow 24 hours or less)
The user's email account is the weakest link in this scenario.

Here are three very good links that provide information on password resets:
http://jtauber.com/blog/2006/03/20/account_management_patterns/
(Don't let users confirm using GET):http://www.artima.com/forums/flat.jsp?forum=106&thread=152805&start=15&msRange=15
http://fishbowl.pastiche.org/archives/docs/PasswordRecovery.pdf
Hope that helps. They sure helped me understand the issue.

Never email a password to the user. Even if it is auto-generated. Best approach (recommend and used by SANS and others):
On the forgot password page, ask
the email/user id and a NEW password
from the user.
Email a link to the stored email
for that account with an activation
link.
When the user clicks on that link,
enable the new password.
If he doesn't click the link within 24 hours or so, disable the link (so that it does not change the password anymore).
Never change the password without the user consent. It means do not email a new password just because someone clicked on the forgot password link and figured out the account name.

Related

Best practices for passwords on registration?

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...

Time limited, or one time use, password reset tokens?

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.

Why should I use an expiring token on 'forgot password' pages?

I am building a forgot password page. I've been reading around and many sources recommend to have users enter their email address, which will then add a token in the DB and send them a link with the token attached as GET variable.
I was curious why that token is really necessary?If the token is expired, anybody with bad intentions and access to your email, can go right back to the forgot password page and enter your email again to get a new password reset link.
I don't see the point of even having a token that expires at some point if somebody has access to your email address. Why should I use an expiring token on 'forgot password' pages?
Let's assume that a person with malicious intent wants access to your account on example.org, but doesn't have access to your email account. Also assume example.org's "forgot my password" algorithm's tokens don't expire.
This person will, if he's at least half-smart, do his research and setup a fake account on example.org and hit the "forgot my password" button and get a reset link himself to find out how these tokens are built (at least, the format they're in).
Then, said person types in your username and clicks the "forgot my password" button that emails you a reset-link that doesn't expire, but they don't care about that - they know the format that the reset-token is in; so, they can just brute force the reset-page with incrementing tokens until they find a valid hit.
This approach, of course, will technically find every reset-request that hasn't been fulfilled but will also find yours too.
If tokens expire, and within a reasonable time limit, the amount of time between the generation of said-token and it's expiration will occur far before the attacker can "guess" it. Of course, there's always the chance that you see the email prior to them guessing it too - but that's far less secure than adding an expiration time =P
You can ask the question the other way round: Why should a reset-link be valid for ever?
When i ask for a password reset link, i will use it normally in about one hour or two. There is no advantage, when i can click the link two years later, probably i won't remember that such a reset-link even exists. And it's easy to request a new link.
On the other hand, if someone in future gains access to my e-mail account, or maybe gets a backup of my e-mails somehow, then he can use the reset-link. Being able to read the e-mails doesn't necessarily mean, that one can login to the e-mail account. There is for example the open e-mail client in the office, a forgotten logout in an internet-cafe, a lost mobile phone...
The token itself should make sure, that an attacker cannot predict the code of a new reset-link. It is better to use a random code, instead of a code that was generated by parameters like username, current time, or e-mail address.
Generally one can say, why use a weaker scheme, if there is a stronger one, when the work for coding is not significantly harder?

Review my design: Password resetting facility for web site

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.

What is safer? Should I send an email with a URL that expires to users to reset their password or should I email a newly generated password?

I was wondering what would be the safer option when users have forgotten their password
Send a randomly generated new password to the email address (all email addresses in my database are confirmed to work).
Or
Send an email with a link that expires within a certain time frame where the user can reset their password.
Aside from the fact the latter uses an extra table, what do you think is safer/better practice?
If you send an email containing the password, it means :
The password will go through some networks (unencrypted) and could be "seen"
The password will stay in the user's mail box
Which can be hacked
And just any one who has access to the computer might take a look
So, sending the password in an email doesn't seem that safe...
As a user, I would feel my password is "safer" with the link that contains some kind of token and expires after a while.
That "expires after a while" part is important, btw : it makes sure that if someone clicks on the link after some time (for instance, someone who accesses the user's mailbox), the link will not be used to generate a new password.
Of course, this means I won't be able to just "search in my mail box" to find the password -- but I can always ask for a new one I have forgotten it again ^^
Rather baffled by the other answers here. They're exactly the same. Both give access to the user's account, both are sent in plain text, and both are in common use. Pick whichever you prefer.
Enforce an immediate password change once they use the link/password, and have the link/password expire after 24-72 hours.
Send an email with a link that expires within a certain time frame where the user can reset their password.
That one, definitely.
E-mail is always in the clear (potentially your site connection may not be), and can touch more machines. Keep passwords out of e-mail. The temporary reset token also means that if the mailbox is hacked later on, the token is of no use any more.
Aside from the fact the latter uses an extra table,
It doesn't have to. You can generate a cryptographic token authorising a particular user to reset a password within a certain time frame; no extra data required.
An example using a HMAC based message authentication code (fancy hashing):
details= user_id+' '+token_expiry_timestamp
mac= hmac_sha2(server_secret, details)
token= details+' '+mac
then send the token to the user as part of the clickable URL in a mail. When you receive a click back, work out what the mac should be for that user and time with your server-side secret, and check that against the passed-in mac. If it matches, it must be a password request you signed earlier.
user_id, token_expiry_timestamp, mac= token split on ' '
details= user_id+' '+token_expiry_timestamp
if hmac_sha2(server_secret, details)!=mac
complain
else if token_expiry_timestamp<now
complain
else
allow password for user_id to be changed
This requires no state, but you should use shorter expire times as the tokens could be used multiple times if you do not record usage.
One difference that people seem to have neglected is that - taking a web application for example - a password reset option is usually open to anyone who accesses a site and knows the username/login of the account that they want to reset the password for.
By sending a link in an email that the user has to click in order to be able to reset their password, you avoid letting users accidentally or maliciously reset other people's passwords - all that will happen is they receive an email that ends with, "If you did not ask to reset your password, please ignore this email."
Even if it's not a security risk per se, resetting passwords without confirmation could be a major annoyance.
Obviously the latter is much safer. Email is like a postcard. Pretty much anyone can read it who wants to. Also, once the password is changed, send an email to close the loop.
As long as the URL doesn't ask for a password or some such, it still is better than the randomly sent password but only because it doesn't leave the password in plain text in an Inbox.
In other words, the link reduces the window of opportunity.
I've always been a fan of setting a hashcode and giving them a link.
Sending an email to the user afterwards letting them know they requested a password recovery link, and after they set one telling them their password was changed is usually a good courtesy in case there was a violation.
A user will very quickly react to an email saying their password was changed if they didn't mean to do it.
Unfortunately there is no real "SAFE" way. Security Questions an pins can help but are never truly secure.
Send them an email with a random, one time use,
password.
Force them to change the
password when they first arrive.
Notify them that they changed their
password.
Sending the random password is as much of a risk as sending the link. i.e. anyone can get the email first and log in as the user the first time.
By forcing the change, whoever gets their first can not get there again without setting a password.
Notifying the user of the change tells them that the password has been changed, and this can happen before the attacker can actually log in and change the notification email.
So, if someone were to get to the site first, the original email to the user will no longer work, as the original password is no longer valid. Also, they'll be notified of the password change.
This provides an opportunity for them to notify sys admins when they show up and find they can not log in to their account.
None of these stop the ability of a person intercepting the email and getting SOME access, but at least it lets the original, vested, user know something is amiss.
Some have stated that both are equivalent - this is not true for following reasons:
1) With reset link if attacker has access to email and consequently uses reset link to change password, they will alert user even if the actual reset email and notifications are deleted by attacker. With mailing password if user requests reset and attacker sees the random password (even much later), then attacker can access user's account on your site without alerting user.
2) Also if you mail a password the user may be tempted to re-use the password on other sites and attacker with access to email has access to other sites even if the other sites were not vulnerable to account take over via account recovery.
With both random password sent in email and reset link, if attacker controls user's email, they have access to user's account. What you can do in this case, depends on how many handles on the user you have - for example, if you have their primary and alternate email address, then you should send notifications to both email accounts when reset is requested and used or if they have a phone, you could send them a text in addition to email, etc. You can monitor usage itself but that is harder.
A couple of other issues:
Can the link be used multiple times? Apart from expiring and having unpredictable value (with attached MAC so it can be verified without server state), you may want to have an internal alert go off if an attempt is made to reset password on an account multiple times (register success/failure, remote ip address, timestamp, etc) and abort after first and put the account in some inactive state.
It would be a good idea to see how much abuse is happening to see if you need more defense mechanisms to prevent account takeovers via your account recovery flows (depends on the value of an account).
Also very important in this case to keep up-to-date on email addresses and other contact information if you can (email addresses do get recycled when not used) and how email address or other such information can be updated/added and notifications.
As always make sure your notifications (text, link, landing page) don't make it easy for phishers.
Some of these issues of course may not be very relevant unless you have a large site.
Send them a link so they can later reset their password. This forces them to confirm somewhat its actually their account they're resetting the password for. If you reset the password without sending an email, anyone can log in to the site and reset anyone else's password. This creates a denial of service type vulnerability.
Although I may be repeating some answers, I feel compelled to respond because we recently had some issues with faulty password recovery tools. One of my coworker's personal accounts was compromised which allowed our google hosted domain apps to be compromised. Due to undeleted plaintext passwords and stupid password recovery questions that were googleable other accounts were compromised as well.
Suffice it to say, I am a strong adherent to emailed links that expire after 4 hours. I sat there for 4 hours logging into one of our accounts after receiving the link making sure it was still uncompromised. 24-48 hours would be waaaay too long to have to do that. 4 hours was too long. A randomly generated password that the user is required to change upon next login is second best, but it is completely dependent on the user actually logging in. The password is changed permanently, whereas if the user does nothing with the link, the password will not be reset.
There is no perfect solution against a dedicated individual who wants to compromise your system. There are better solutions than others.
Extending from bobince's solution... Here user is required to reenter userId and token on password reset page.
On request for reset password page
urserId = Input userId
token = Randomly generated token (or one time password)
tokenExpire = Decide token expiry date/time
Store in DB tokenExpiry for this urserId
urlToken= MD5 hash value of (urserId + token + tokenExpire)
pwdRestURL = server pwd reset url + "?urlToken=" + urlToken
Send above generated URL and make sure you do not
include either of userId or token in email
Display token to user (This is to be used on password reset page)
.
On password reset page (using above pwdRestURL URL)
urlToken = Token from URL request
userId = Input userId
token = Input token
tokenExpiry = tokenExpiry from DB for this user
resetToken = MD5 hash value of (urserId + token + tokenExpire)
IF
resetToken == urlToken
AND tokenExpiry for user is valid
THEN
Clear tokenExpiry
Allow user to change password
ELSE
Display Error
END IF
.
Advantages of above approach:
Even if email is some how exposed in
network, no one can reset password
without knowing the userId and token.
Token has an expiry period
No clear test personal information is
relayed over email
i agree with will's process.
however if you only choose between the options you have given, although both options are essentially the same in that you're sending information via email, i think the latter is a more common method.
if a hacker was to request a new password, the user's old password would no longer work. at least with the latter option it doesn't actually change any user details.
Everybody except for ceeyajoz is using flawed logic. Its hard to think about security.
Both cases use of email which is in plain text. Both are equally insecure when email gets hacked.
It doesn't matter if the URL expires since the email is hacked the hacker can just request for another password reset URL. If the temporary password has changed, the hacker could just request a new one. Either way you are screwed.
So I say just send the password, this way its one less step for the user to pick a new one.
EDIT
When I said "send the password" it was in the context of the OP where you send a new random password.

Resources