Is there a security loophole here? - security

I have a web app which allows users to sign in to their accounts.
When they forget their password, or when they want to confirm their email, they receive an email with a one-time link (this link has a one-time code in its query string ie example.com?code=encrypt(1234)), which expires after a given time.
Now having this link will grant you the ability to change the user's password. Is this a security threat? Every package sent to and from this webapp is non-deterministically encrypted.
My questions in short:
Is it a problem, that I am using the same link to confirm an email, and reset the password depending on the situation?
Is it a problem that this one-time code is being sent as an encrypted string. What I mean is if the code is actually a string such as h4drMfH/iVSPJGP0+zFlNQw......5bqnwU7Pdbo= is there any way someone could predict the algorithm, or crack it if they know that the string codes for four digits? Ie could they work backwards to find the algorithm. Note that the result of encrypt(123) will be different if you do it twice.
Could someone get their hands on it by using the fact that the code is part of the querystring used by my web app?
Appreciated.

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.

Security concerns regarding username / password vs secret URL

I have a simple site with a sign-up form. Currently the user can complement their registration with (non-critical, "low security") information not available at the time of the sign-up, through a personal (secret) URL.
I.e., once they click submit, they get a message like:
Thanks for signing up. You can complement your registration by adding information through this personal URL:
http://www.example.com/extra_info/cwm8iue2gi
Now, my client asks me to extend the application to allow users to change their registration completely, including more sensitive information such as billing address etc.
My question: Are there any security issues with having a secret URL instead of a full username / password system?
The only concern I can come up with is that URLs are stored in the browser history. This doesn't worry me much though. Am I missing something?
It's not the end of the world if someone changes some other users registration info. (It would just involve some extra manual labor.) I will not go through the extent of setting up https for this application.
This approach is not appropriate for sensitive information because it's part of the HTTP request URL, which is not encrypted and shows up in many places such as proxy and other server logs. Even using HTTPS, you can't encrypt this part of the payload, so it's not an appropriate way to pass the token.
BTW, another problem with this scheme is if you send the URL to the user via email. That opens up several more avenues for attack.
A better scheme would require some small secret that is not in the email. But it can be challenging to decide what that secret should be. Usually the answer is: password.
Another potential problem lies with the users themselves. Most folks realize that a password is something they should try to protect. However, how many users are likely to recognize that they ought to be making some sort of effort to protect your secret URL?
The problem here is that although it is hard to guess the URL for any specific user, given enough users it becomes relatively easy to guess a correct url for SOME user.
This would be a classic example of a birthday attack.
ETA: Missed the part about the size of the secret, so this doesn't really apply in your case, but will leave the answer here since it might apply in the more general case.
can complement their registration with (non-critical, "low security") information
It's hard to imagine what user-supplied information really is "low-security"; even if you are asking for a password and a username from your customers you are potenitally violating a duty of care to your customers; a large propertion of users will use the same username/password on multiple sites. Any information about your users and potentially a lot of information about transactions can be used by a third party to compromise the identity of that user.
Any information about the user should be supplied in an enctypted format (e.g. via https). And you should take appropriate measures to protect the data you store (e.g. hashing passwords).
Your idea of using a secret URL, means that only you, the user, anyone on the same network as the user, in the vicinity of a user on wifi, connected to any network between you and the user, or whom has access to the users hardware will know the URL. Of course that's not considering the possibility of someone trying a brute force attack against the URLs.
C.
The secret URL means nothing if you're not using SSL. If you're still having the end-user transmit their identifying information across the Internet in the clear, then it doesn't matter how you're letting them in: They are still exposed.
The "secret URL" is often referred to as security by obscurity. The issue is that it is super simple to write a script that will attempt various combinations of letters, symbols, and numbers to brute force hack this scheme.
So if any sensitive information is stored you should definitely use at least a username and password to secure it.

sending username and password through email after user registration in web application

What is your opinion on sending the username and password to their email address when they register on our website..this way if they forget the password in the future, they can look it up in their email...also we wont have to implent the forget/reset password scenario (we are close to release)..
is this approach safe enough?
My second question is that basically on our site, the user fills out certain forms and enter some information like their name, address, phone number, income information and such personal information..at the end, when they submit the application, we are thinking of emailing them a summary of all this information like their name, address etc so that they have it for their records..
is this ok..safe enough..what are the concerns
Never send a password or other sensitive information in the clear. That includes e-mail. You should also be storing as little of this as possible in a recoverable format. Unencrypted communication, especially e-mail, is easily tampered with, and you don't want the wrong people getting at passwords.
If possible:
Store your passwords in a salted hash, so the original text is unrecoverable, and thus unbreakable by anything short of a brute force attack. If the user forgets his/her password, make them reset it and send a temporary password (which they are required to change upon login) or a confirmation link (which, again, prompts for a new password) via e-mail.
Never send anything sensitive via e-mail; if the user needs information, make them go to your site to get it. You are using HTTPS, right?
People often share passwords across sites. So you should assume the same password works for the customer's online banking, and you should never send it by e-mail or provide a way for (someone pretending to be) the customer to retrieve it.
It's fine to send them a confirmation e-mail with their username - this is useful.
Remember, if you e-mail them their password they're likely to forget about that e-mail, or just delete it. So you need another password reset mechanism anyway.
The best way to handle the "forgotten password" case is for the user to request you to e-mail the user a link; when they click the link you allow them to type in a new password.
Regarding personal information (address, income etc): why would anyone want this mailed to them? They already know it! You're just sending private data unencrypted over the internet for no reason.
My rule of thumb would be - if you're OK writing it on a postcard and sending it through the mail, then it's OK for standard Email. I don't think income information would fall in that category for most people.
As for passwords, if they can't remember them in the first place, they won't be able to find the Email you sent them with the password in it, and it's an admission of storing it in the clear. I would avoid it and give them the means to reset - they will need that anyway.
The concern is definitely in the sending of the email with the password. If it is not properly encrypted, someone could potentially sniff the packets from the email being sent and recover the password. Also, the person could potentially have a hijacked email account. If it's not a big deal if someone steals the password then you may not have to worry, but otherwise I would NOT send any unencrypted passwords via email.
Edit: To address your second question, I wouldn't even email that. I would instead send a link so that they can easily see their profile/information when they log in.
I tell people to think of email like a postcard -- an employee of any company that handles it between the sender and the recipient can read it.
I agree with the top answer and have this to add: every time I receive a signup confirmation email that contains my password I delete the email and strongly consider never using that web service again. To me, it indicates a lack of security & privacy consciousness.
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...
Most company simply do not include Username password combination due to the security of the external email client. Any numbers of users could brute force or guess the password to the email account of another users which would allow the hacker to view the email of your site. Then the hacker could wreak havoc on your site as well
I'd say providing a forgotten password function will still be vital as not everybody will be guaranteed to keep all there emails (or even be able find them later on)...
I have three rules concerning passwords:
Don’t store passwords in plain text in the database
Why should people trust you with that kind of information? You may only have good intentions, but big companies have failed before, so you're at risk too.
Don’t use password reminders
Password reminders are not worth it. They are easy to guess from people in your entourage and you often forget them. There are better ways to reset a password.
Always offer to send a new password by email
This is the most secure way of retrieving passwords. You should force the user to change the password once logged in with the new password.
As mentioned in comments, you might want to look at OpenID. The most secure way to manage passwords is to eliminate them.
I build an Web Application to send sensitive information by email. It's not UI perfect but it's really secure and working very fine.
There an outlook plugin, API to connect external website and the WebSite.
The concept is the message received in your mailbox are not in clear text. It's an HTML email with a link. You need to click the link to access the content of the email. When it's access one time, the message are destroy.
The message are stock in a crypted database on our side. You can configure a password that are know only by the two part to open the message online, or receive an password (Random 6 number) by SMS.
It's very simple to implement by API.
There is a sample
// https://www.secure-exchanges.com/API.aspx
List<string> files = new List<string>();
files.Add(originalFilePath);
string input = $"{body}";
string inputSubject = $"Your {subject}";
SendMessageAnswer answer = MessageHelper.EncryptMessage(new EncryptMessageArgs(GlobalSettings.bindingSecure, GlobalSettings.serial, GlobalSettings.ApiUser, GlobalSettings.ApiPassword, input, inputSubject + " - to open", recipient1, "", password, null, SecureExchangesSDK.SecureExchanges.SendMethodEnum.onlyEmail, false, true, true, "fr-CA", 1, 5)
{
FilesPath = files
});
if (answer == null || answer.Status != 200)
{
throw new Exception($"Impossible d'envoyé un message : {methodName}");
}

What is the best "forgot my password" method? [duplicate]

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.

Resources