When a user wants to do a password reset, an email is sent with an unique URL so he can reset it. Like this:
website.com/forgot.php?email'.$email.'&hash='.$thehash
$thehash is a unique hash for every user stored in the database.
The problem is that $thehash is stored in the database just the way it´s used in the URL. That´s just as stupid as storing the passwords in plain text. If someone get´s access to the database it doesn't matter that I have my passwords stored with sha512 and a secure salt, the attacker can just get access to all account using the values (email and hash) all found in the database and change passwords for users.
When I hashed user passwords the user had one part of information that could not be found in the database, the plaintext password so it worked out. But now, I have no idea what to do since I have nothing unique not found in the database. So what is a good way solve this? How do I securely store hashes?
The problem isn't with how you're storing hashes, it's with how the reset link works.
You don't want to use the hash to authenticate a user for password resets, for the reasons you mentioned.
Use a perishable token instead. Whenever a user requests a password reset, generate a token (256-bit should be enough) and store its hash in your database, along with the user who requested it, and the token creation datetime. Put that token in the reset link (instead of the email+hash). When the user clicks the link, your server will receive the token, find the corresponding user and it'll be safe to change the password.
By only storing the token's hash in your database, but using the unhashed token in the email link, you're making sure that even if the attacker still has access to your database, he won't be able to forge his own reset links.
By comparing the time when the user clicked the link with the datetime stored when the token was generated, you'll be able to control how long the reset link is valid (and avoid situations where a user forgets to delete the email, gets his email account compromised, and have the attacker use the reset link).
Check this Authlogic Password Reset Tutorial for a full implementation.
When a user wants to do a password reset...
I will refer you to the OWASP Forgot Password Cheat Sheet, which in essence states:
1 Gather Identity Data or Security Questions
2 Verify Security Questions
And an alternate to giving the users a hash of something:
3 Send a Token Over a Side-Channel: "After step 2, lock out the user's account immediately. Then email or SMS the user a randomly-generated code having 8 or more characters... It is also a good idea to have the random code which your system generates to only have a limited validity period, say no more than 20 minutes or so... Of course, by all means, once a user's password has been reset, the randomly-generated token should no longer be valid..."
Allow me to add here that you can email the user the original token, but store a hash of it in the database using exactly the same protections you use for normal passwords, i.e PBKDF2/BCrypt/SCrypt, and storing only the resultant hash in the database. Then when the user uses the password reset email, if it's still within the very short time window, take whatever they give you, and use your password_verify() function to compare it to the reset token hash.
4 Allow user to change password
Thus, your reset tokens are protected by:
Only being issued upon a validated request
Only being valid for a few minutes
i.e. hopefully too short a time for someone who's stealing your database backups to be able to use them!
Optionally being protected from rogue database access by your password hashing mechanism, just like any other password.
Your reset tokens are obviously not protected from an attacker with access to the user's email account, or who can change the listed email account, while the token is active.
Password reset via security questions as a whole is obviously not protected from an attacker who knows or can compromise the security answers and who has access to (or who can change) the user's listed email account.
#relentless is correct. If the attacked gains access to the database isn't it true that he/she would be able to reset the passwords regardless of whether they had the keys? You're also assuming they've either gained access to the user's e-mail account or guessed the hash entirely. Another thing to consider, you don't necessarily have to store the hashes in the database. Consider this: let's say the hash is created by combining the user's e-mail with a key you've previously determined. When the reset page loads simply rehash the e-mail and key and see if it matches the hash in the query string.
Why don't you pass $thehash as a session variable, then check it to the function your directed to 'if it is set?', if it is, then execute, after then delete the session variable.
How do I securely store hashes?
...
So what is a good way solve this? How do I securely store hashes?
This is the "Unattended Key Storage" problem, and its a problem without a solution. See Peter Gutmann Engineering Security.
John Steven of OWASP provides one of the better write ups related to password hashing and storage. He takes you through the various threats and explains why things are done a certain way. See
Password Storage Cheat Sheet
Secure Password Storage Threat Model
There are multiple posts on the Internet regarding this issue, but here's how I see it.
(Do correct me if I'm wrong in what follows...)
A hacker can only do permanent damage if your actual DB and login credentials get known/compromised, where the data can be changed. Otherwise, the data remains safe even by changing the email address in the URL. Just as long as you don't give a potential hacker a back door to change the Email address; that's gold in its own right.
If passwords are properly stored using a one-way irreversible hashing method, then they are just that; irreversible and chances are rather great that they cannot be put back together. If a user's password has been compromised, then that will be a red flag for you to re-think the way you're using your DB.
At best, even if a user's password gets changed, give the user a method to change it again, then set a column to track how many times it has been changed. If it keeps changing too often, then again that will be another red flag. I have used a similar method to what you're using now and nothing got changed in the DB even when changing the Email address in the URL; everything must match.
Plus, even if someone did change a user's password, depending on what type of permissions or access you've given your users, what's the hacker going to do, change the password again?
The URL should contain the Email associated with the account, the hashed key stored in the DB and then retrieved, and will only work if a hacker has gotten hold of the user's Email account credentials and has gotten access to what should be a unique link.
I don't know which hashing method you're presently using, but there are a few that many suggest using.
crypt()
bcrypt()
scrypt()
PBKDF2
PBKDF2 on PHP.net
PHP 5.5's password_hash() function.
Other links:
PBKDF2 For PHP
Footnotes:
Quoting owlstead if I may: (which I do agree with)
"The best methods are PBKDF's such as PBKDF2, bcrypt and scrypt. crypt should not be used if possible, password_hash() is an implementation of crypt and bcrypt and not a separate algorithm. Users passwords can always be compromised if they choose a bad password. One way cryptographic hashes or PBKDF's cannot be reversed, but they can be brute forced (e.g. using a dictionary attack)"
Related
We all know we shouldn't be storing user passwords as plain text on
the database. However I have seen some sites that have implemented
a send-forgotten-password feature. So if I ask my password, I type
my email and they send the password.
Note: I'm not talking about a password change (http://sample.com/forgot_pass?token=ddm39fhksnc)
How do these sites achieve it? They store plain passwords on their
databases (maybe a different database) because as far as I know you
can't reverse a password hash to the original string it was built from...
Is there anyway this feature can be implemented securely? Or I should
convince clients to stick with forgot-pass-link method.
Thanks.
If they are able to send you your password then they are storing your password in plain text.
Note that you can find out the original password from the hash if a deprecated hash function was used (like md5).
There is no way of doing it securely. If your database gets breached the attacker will be able to read out all the passwords and corresponding email addresses/usernames. If your users re-use the same password for different sites (which most people do) what can happen.
Even if there would be a "secure" way of finding out the password from its hash, what stops the attacker from doing the same?
Such a feature cannot be implemented securely. If the application can retrieve the original password (e.g. from an encrypted password), a successful attacker can retrieve the passwords as well. That's why one should use a hash function like BCrypt, SCrypt or PBKDF2.
Another weakness is sending the password at all per e-mail. A better way is to send a token, and let the user choose his own password after confirmation. The same code can be used to register a new user and to reset a password.
You open the door to major security issues if you store the actual password in the database. But if in fact you must resend their password to them, one way you could do it, is when they register, store the password in a text file located outside of the public directory.
Then create some sort of naming logic that associates the record to the text file. And then from there, make it so the only way you can access that text file is with the proper security checks in place, and then via your script.
My solution is adding two field in table "user" in my database: "token" field and "expired_time" field.
When user send a request to reset password, your application will update token string and expired time to reset password for that user.
Send an email with link has username (or email, or id,...) and token like http://example.com/resetpassword.php?id=userid&token=token
In resetpassword.php, you will check authentication by userid, token and in expired time (maybe 5 minutes)
Allow user to change their password.
This is my solution, I hope it'll helpful :)
Maybe silly question...,
but i'm asking myself if it's useful to store the password (plain text) in the database once i have hashed it using password_hash() function...
Since password_verify() only uses hashnSalt to check if pass is correct.
once the "Villain" obtain access to the db he doesn't have plain text password (will ask him time to bruteforce them)
If the user forget his password he can't get it back : only way is to recreate a new pass.
i'm on a 50/50..
Do NOT EVER under any circumstances store plaintext passwords. Doing this defeats the entire purpose of hashing the password in the first place.
The entire purpose of using a one way hasing algorithm is that if a hacker gets into your site and steals your database it is virtually impossible for that hacker to obtain the passwords for your users without using bruteforce or tables. (The tables risk is also mitigated by the salt introduced by the password_hash() function.)
You should never be able to retrieve your users' passwords, even as the site owner/operator (if you can, then a hacker can too). That's why any website that will send you your password instead of having you reset it is a red flag immediately as they are not storing your credentials securely.
The correct way to handle this is if a user forgets their password you send a temporary link which allows the user to create a new password.
First: never store plain text passwords! Probably the user uses more than one place the same password, and you would give the login information to other services for the attacker.
I would sabe only the created hash and if the user needs to remember the password he/she would have to reset it with a new one. The method to do this you can choose, but sending an email with a unique temporary link the best solution.
if a user forget his password, you can send him/her email with a link to his page and ask him to change his password. or you can set a new password for the user and send it to that user
For user's password entered in a form before post, I simply just do
<?php
$pass=crypt($_POST['memberpwd']);
?>
But how can I later get the plaintext of that password in case the user may request ? Thank you.
EDIT: I found How can I encrypt password data in a database using PHP? and http://www.securityfocus.com/blogs/262, but I would just want to learn about this at a more basic level to understand the ways as to how it actually works.
Always store a password in a database using only a hash of (the pasword and a randomly generated salt) (googling for these terms combined with PHP should yield some useful results).
You never restore a plaintext version of the password, you provide means for the user to choose a new password after the user clicks on a unique referral url you only send out by email automatically on request.
(Very basic security).
EDIT:
I'll explain why you need the salt. Suppose someone compromised your database and has the user table with simple (e.g. MD5) password hashes. He (or she) can now simply launch a dictionary attack against the whole table with simple password combinations (e.g. select * from users where pw_hash = ...). If you use a randomly generated (but stored) salt to be used in the hashing of the password, this brute forcing attack gets exponentially harder to exploit.
The first thing to understand is exactly what crypt() does. Start off by reading this: http://php.net/manual/en/function.crypt.php
Essentially, crypt() is a hashing function. To be more exact, it is a one-way hashing function. As such you aren't going to be able to recover the password from it.
In today's world when a user has "forgotten" their password the best way of handling it is to create a new password, store that and email it to them.
By doing this you are ensuring that if they used the same password for multiple services you aren't inadvertently exposing those other services to nefarious people. Second, if it isn't the original user requesting the password, the next time they try to log in they'll notice their password doesn't work anymore.
Look at my answer to a related question.
Basically, you never ever ever store plaintext passwords. Databases can be compromised and most users use a universal password, which will allow the attacked to have access to tons of data. And it will be your fault (to some extent).
Hashing (in your case) works on this principle:
if hash(session.password) == database.hashed_password:
# You can safely assume session.password == database.password.
# Notice that I don't store database.password, but instead store:
#
# database.hashed_password = hash(register.password)
#
# when the user registers. That way nobody will ever know the password.
You can also use salts to make your hashes more secure. The same principle holds:
if hash(session.password + 'imasalt') == database.hashed_password_with_salt:
# Same as above.
Common practice is to use a one-way hash algorithm like SHA (and MD5 in the past). These cannot be reversed. If your user need to recover a password, the system usually resets it and tell the user the new password.
If you want two-way encryption (highly discouraged to store passwords) then your application will need to keep a secret (or have the user provide it).
Take a look at mcrypt http://php.net/manual/en/book.mcrypt.php
You don't. If they've forgotten their password, you should reset it and send them an email to create a new one, or something along those lines. Storing passwords in a way that means they can be decrypted and retrieved is very insecure and could, possibly, be illegal in some countries (I'm not a lawyer).
I have a small community website and I need to implement some sort of forgotten password function. I currently store the passwords in the DB, encrypted with MD5.
Is it possible to sort of 'decrypt' and send it to user via email or would I need to have a password reset page?
An MD5 hashed password is not reversible. (MD5 is hashing, and not really encrypting, so there's a subtle difference). And yes you'll definitely want to provide a password "reset" process (and not simply email the password).
To give you a high level workflow for secure password resets...
When user asks to reset their password, make them enter their email address
Don't indicate if that email address was valid or not (just tell them that an email was dispatched). This is open for debate as it lowers usability (i.e. I have no idea which email I registered with) but it offers less information to people trying to gather information on which emails are actually registered on your site.
Generate a token (maybe hash a timestamp with a salt) and store it into the database in the user's record.
Send an email to the user along with a link to your https reset page (token and email address in the url).
Use the token and email address to validate the user.
Let them choose a new password, replacing the old one.
Additionally, it's a good idea to expire those tokens after a certain time frame, usually 24 hours.
Optionally, record how many "forgot" attempts have happened, and perhaps implement more complex functionality if people are requesting a ton of emails.
Optionally, record (in a separate table) the IP address of the individual requesting the reset. Increment a count from that IP. If it ever reaches more than, say, 10... Ignore their future requests.
To give you a little more detail into hashing...
When you hash a value like a password using the md5() function in PHP, the final value is going to be the same for that password no matter which server you run it on. (So there's one difference we can see right away between hashing and encryption... There's no private/public key involved).
So this is where you'll see people mention a vulnerability to rainbow tables. A very basic explanation of a rainbow table is... You md5() hash a bunch of dictionary words (weak passwords) in order to get their md5() hashed values. Put those in a database table (rainbow table).
Now, if you compromise a web site's database, you can run the users' hashed passwords against your rainbow table to (in essence) "reverse" the hash back to a password. (You're not really "reversing" the hash... But you get the idea).
That's where "salting" your passwords is best practice. This means (again, very basic idea here) that you append a random value to the users' passwords before you hash it. Now, when the rainbow table is run against your database, it's not as easily "reversed" because the md5() hash of "password" is different than "password384746".
Here's a nice SO Q/A that should help. Secure hash and salt for PHP passwords
According to this post The definitive guide to forms based website authentication, for step 3. and 4., I'm not sure you should send the same token you are storing.
I guess you must send the token, then hash it and stored the hashed token in DB. Otherwise, if your database is compromised, one can have access to the reset password page.
To summarize :
$token = md5(microtime (TRUE)*100000);
$tokenToSendInMail = $token;
$tokenToStoreInDB = hash($token);
where hash is a hashing algorithm.
No, MD5 is irreversible. The point of hashing passwords is to make it so an attacker who gets access to your database can't access everyone's passwords.
That said, MD5 (particularly unsalted MD5) can generally be attacked using a rainbow table. For security, you're better off using bcrypt.
You cannot decrypt the password, and you shouldn't even consider sending a password to a user via plaintext. (That is the #1 way to make me never ever use a site again; it's a GIGANTIC security hole.) Provide a password reset page that is triggered from a link containing a time-associated key that is sent to the user's password recovery email; that's the current state of the art in password recovery.
The best thing for you to do is request people submit their email address when registering. Then if they forget, have a forgot password link which resets their password with a random value which is emailed to them so they can gain access and then change their password back to something more memorable. This way you don't need to compromise the security.
You could have a link which they just need to submit their username into, butfor better security you should have a question and answer or memorable word.
As Marcus Reed stated, in 2015/2016 if you have PHP version >=5.5 don't use MD5, password_hash() and password_verify() provide an easy and secure hashing for your password with the ability to provide a cost and automatically salts the hash.
I don't have the ability to vote or comment currently which is why I'm providing a definitive statement to avoid confusion.
MD5 is intended to be a one-way hash. You will need to have them reset their password.
Write a page that accepts the md5 and email address as a get paramaeter and looks in the db for the email and md5'd password. Following Jared Cobb notes, that should get you on the right path. i just added some examples as well
eg url to send http://yourdomain.com/resetpassword.php?code=md5codesentviaemail
$code = isset($_GET['code']) ? $_GET['code'] : '';
$email = isset($_GET['email']) ? $_GET['email'] : '';
$checkPw = '';
if(empty($code) || empty($email))
{
die();
}
$sqlQuery = 'SELECT * FROM users WHERE email = "'.$email.'";
//remember to check for sql injections
//then get the results as an array, i use a database class eg $user
if(!empty($user['password']))
{
$checkPw = md5($user['password']);
}else
{
die();
}
if($checkPw !== $code)
{
die();
}else
{
//display form for user to change password
}
this should be sufficient enough for you to know that the user is a valid user and change his password
Use php's built in password_verify and password_hash.
No you cannot decrypt it. that is the whole idea.
You would need to send them a temp password and for them to reset it.
You'll need to do a password reset page. There's no way in PHP to decrypt MD5.
MD5 is a one way function. You can't decrypt it. SO you need to have a password reset page.
I am using md5 to encrypt the passwords in my project.
When user clicks on forgot password and submits his email,I have to send His password to him.
But the password is encrypted using md5.Generating new password should not do.Because In this project admin can see all the details of the user. So i have to show the original password to Admin. So The initial password is very important. SO how can i decrypt the password or any other way to send him original password?
Thanks in advance...
Hashes are not designed to be decrypted, which is why they're often referred to as "one-way hashes" instead of just hashes.
Instead, either...
Generate a new password, hash that, store the new password hash in place of the old one, and email the newly generated password to the user.
Generate a new password, hash it, store it in a field for temporary passwords, and then when the user logs in with that password, prompt them to enter a permanent new password.
Generate a nonce, store it in a field for the nonce, and email the user a link with that nonce which will give them access to a page to enter a new password.
The third option is probably the best all around, since it doesn't leave an actual password (temporary or not) in plain view to someone reading the user's email, and since it utilizes a nonce, once it has been used it can't be used again by a malicious user.
The reason hashing is used for passwords is specifically to prevent them from being stored in a form where a malicious user could determine the password simply by looking at the database.
Edit:
"So i have to show the original password to Admin."
If you are hashing the password, this is not possible. In general, it is actually a bad idea to allow administrators to see users' passwords, because a large percentage of users tend to utilize the same password for multiple things, and the administrator of one thing (say, a company network) is probably not the administrator of many other things (say, a user's online banking system).
MD5 is not an encryption algorithm, it is a hashing algorithm. The two are not the same; encryption is designed to be reversible (hence the complementary term "decryption"), whereas hashing is designed to be one-way only.
You can't. The reason cryptographic hashes[1] are referred to as "non-reversible" is that they can't be reversed. Which is the entire point of using them for password storage - it means that, if a Bad Guy gets his hands on the password database, he can't just reverse the hash to find out what all the passwords are.
I see from your edit that your intent is to display the user's password to the admin user(s) rather than for password recovery by the user himself. This is a Very Bad Idea. Many users attempt to ease the burden of remembering passwords by using the same password for multiple systems, which means that displaying their password in your system has a high probability of compromising their accounts on other systems.
True story: Back in 2000, I took a job at a startup that produced voicemail systems. To introduce me to the product on my first day, the IT director had me create a voicemail account, which I did, then he brought it up in the admin interface. I just about died when I saw my voicemail PIN displayed on the screen for all to see. Partly because it was shockingly bad security practice, but mostly because, even though he didn't know it, he now knew the PIN for my ATM card. That's just bad, bad, bad all around. Don't do that.
[1] MD5 is a hashing algorithm, not an encryption algorithm. The key difference between the two is that, for any given hashing algorithm, there are an infinite number of inputs which will produce the same output (which is why it's not reversible), while encryption has a one-to-one correspondence of input to output.
If the password has been hashed then you'll probably have to create a random password and send that to the user. Then, once they've logged in, take them to the Change Password screen so they can change their password to something more memorable.
One particular purpose (among others) of a hash value is that it's irreversible, if it works perfectly.
The most common way for a "forgot password" functionality is, to generate a new password and tell your user to change it as soon as possible.
Just adding this as a sidenote:
While you cannot "unhash" the MD5 hash, you can look it up in a Rainbow table. That might allow you to send the original plaintext password to the user. I am not suggesting to do that though, because it's just a waste of resources compared to just creating a new password and sending that to the user instead.
From http://en.wikipedia.org/wiki/Rainbow_table:
A rainbow table is a lookup table offering a time-memory tradeoff used in recovering the plaintext password from a password hash generated by a hash function, often a cryptographic hash function. A common application is to make attacks against hashed passwords feasible. A salt is often employed with hashed passwords to make this attack more difficult, often infeasible.
Also see the comments below for additional notes.