I have a LAMP (PHP) website which is becoming popular.
I played it safe by storing the user passwords as md5 hashes.
But I now see that's not secure; I should have salted the md5 hash - because it's currently possible to decode unsalted md5 hashes using rainbow tables.
What can I do?
I don't want to make everyone type a new password.
You can do a "2 step hashing" instead of creating a hash in a single step.
You could append each password hash to the username, and then hash it again. This will create an undecryptable hash thats salted with unique informations.
The usual process of salting is
salt+PWD -> hash
You could do something like:
PWD -> Hash -> UserID+Hash -> Hash
(Note the UserID was only picked so a unique salt for each double hash exists... Feel free to make your salt more complex)
You can salt them on the fly. Add a piece of code so that, when someone logs in, it does the normal process (computes the MD5 sum of the password and checks it against the stored hash) and if that succeeds, recompute a salted version of the hash from the clear-text password they entered, and store it in the password file.
The only wrinkle is that you'll need to add an indicator for whether each MD5 is salted or not, since you'll have a mix of both for a while. Or, for a minor loss of security, you can check each password salted and unsalted and if either one hits, accept the login. Of course, if you detect that it was unsalted, then you upgrade at that point.
The answer is simple, make sure the keep a record or some sort of flag of which users have passwords on the new system of hashing, when they next login, authenticate them, calculate the new hash, flip the flag.
Now whenever someone logs in and the flag is set, authenticate them with the new hash.
Why not add a new column new_pwd to your user table, which stores the result of md5($originallyHashOfPwd . $salt). You can then precompute new_pwd and once that's done adjust your login checking to compare the result of md5(md5($entered_pwd) . $salt) to what's in new_pwd. Once you're done switching your login checking, delete the old column.
That should stop rainbow-table style attacks.
You can still use a salt. Just calculate another hash from the current hash together with a salt:
$newHash = md5($salt.$oldHash);
For new passwords you then need to use:
$hash = md5($salt.md5($password));
A great way to update the passwords while also making them more secure is to change to using a salted SHA1 for passwords. A SHA1 is harder to create a collision against, and it also has a different string length to MD5. A MD5 is 32 characters long, while a SHA1 is 40 characters long.
To convert these in PHP, you first check the string length of the stored password. If it is 32 characters long, check the password using your old method and afterwards, write a new one using SHA1 to the database.
If I remember correctly, this is precisely how WordPress handled this issue.
Dynamically re-encrypt the passwords when the users log in the next time, i.e. first check whether it’s correct, afterwards encrypt it with a salt and store it again.
You can migrate the passwords by adding a column in your tables to store the new format.
When a user logs in successfully, if the new column is empty, put the stronger password in there and empty out the original column. If the new column has an entry, compare the input to the value in there.
Two options here
Decode the passwords yourself, and re-encode them with a salt (I recommend something a little more fancy than MD5). You should inform the users that you're viewing their passwords unencrypted. It'll probably take a lot of time as well.
Make them retype their passwords, and store those salted and encrypted.
As far as I can see, there is no other way of recovering the passwords.
EDIT:
Although MD5 is a hash and should not be decodable, it can be broken using rainbow tables: with probability almost one, you can find a unique (here's the probability) string of at most, say, 20 characters with a given hash, especially if your character set is limited, say, to alphanumeric. Strictly speaking, this is not decoding. For all practical purposes, it is.
Extra note: producing the rainbow tables, and looking up 1000 password is still going to take a lot of time.
Salt the original hash as mentioned by others. Just a few pointers here:
Salts are better the longer they are. Also if they contain more then just [a-z0-9] but length is better first of all.
If someone already has a copy of your DB and you rehash the same passwords with salt, the rehash the old hash with salt will not work. Instead you really should force users to make a new password.
You should match new passwords (and passwords to be salted) up against various lists of the most commonly used passwords. These are used in "brute force" attacks. Prompt/force the user to change the password.
If you're moving away from MD5, you should go skip simply salting and go to an even better technique called stretching. In particular you should use bcrypt (implemented as PHPASS with php).
Here is a great link on why bcrypt: http://chargen.matasano.com/chargen/2007/9/7/enough-with-the-rainbow-tables-what-you-need-to-know-about-s.html
And here is a short How To:
1. Download the phpass package: http://www.openwall.com/phpass/
2. Look at test.php for examples like the one below:
require 'PasswordHash.php';
$t_hasher = new PasswordHash(8, FALSE);
$correct = 'plaintextpassword';
$hash = $t_hasher->HashPassword($correct);
$check = $t_hasher->CheckPassword($correct, $hash);
If $check===true (which is the case above) then the password is correct.
If your password is 'hello', you would hash it using HashPassword, put the hash in a database, and when a user logs in, call CheckPassword(userenteredpassword,hashInDb) to see if the password is correct
sadly, your only way is to tell your users to renew their passwords.
you could also generate random passwords, but that is the same hassle.
edit
you could just double encode your stored passwords. so your new salted hashing algorithm would be:
md5(md5($new_password).$salt).':'.$salt
to update your old passwords use
md5($old_password.$salt).':'.$salt
to check if a provided password is correct simply use
list($stored_password, $salt) = explode(':', $salted_password);
if(md5(md5($provided_password).$salt) == $stored_password) {
// you are now logged in
}
Related
I was wondering if there was a method to change the way my site hashed passwords. My coder friend wasn't the smartest when he didn't add salts to the sha512 hash. So now it is very insecure and I wish to change that. I was thinking about making some complicated code to rehash when someone who has the old hash type logs in and it would set the variable to true after adding a salt. Or I could take the currently hashed passwords and somehow fuse a salt into them. I would rather not reset my user database if I don't have to. Any idea would help. I am also quite the php noob so please explain if you include code.
It is Hashed using this method.
<?php hash('sha512',"passwordhere") ?>
Alter your user table to include a 'salt' column, default value of 'NULL'.
Alter your login code to check if the user has a salt:
If yes, compare the salted hashes and log in
If no:
Compare the unsalted hashes.
Generate a random salt.
Generate your salty hash.
Store your new salt and hash in the database.
Continue the login process.
Of course, you will also need to update your code for registration, password change/recovery, etc.
Alternatively, instead of a 'salt' column you could put in a 'hash_ver' column and use that to determine which validation method to use and when to update the hash. That way if you wish to use a hashing method that packs the salt in with the hash like bcrypt you don't get stuck trying to figure out what type of hash you're dealing with.
Every password-storing-system must have the option to switch to a better hash algorithm, your problem is not a one-time migration problem. In the answer to this question i tried to point out the necessary steps.
Note: Fast hash algorithms like SHA-* are not appropriate to hash passwords, instead switch directly to a slow key-derivation function like BCrypt. The new PHP function password_hash() will make hashing easy (it will generate a safe salt for you), and is "future proof", also it will make switching in future possible.
$old_hash = hash('sha512',"passwordhere");
$salt = ''; // Generate salt here
$new_hash = hash('sha512', $old_hash.$salt) ;
I'm using phpass's bcrypt functionality to hash a password on my site.
Now, it wouldn't really work. Trying to compare with the CheckPassword function wouldn't work. I made a vast debugging of every string coming out through every function I used to decrypt the hashes and came to the conclusion that the hash generated by bcrypt is pretty random. So, the newly generated hash of the plaintext password would never match the one in my database. Is that true? If so, how the hell do I make it work? Source code is rather simple.
// when creating user
<db insert code>$hash->HashPassword($_POST['password']);
// when logging in
return $hash->CheckPassword($user->password, $_POST['password']);
Edit:
The problem is you have the order wrong, you need the password, then the stored hash.
$check = $hasher->CheckPassword($password, $stored_hash);
Source
This matters, as I said before (below) the stored hash is used to decide how to hash the password to compare, hence your wrong argument order will cause failure.
Answer from before:
You don't decrypt a hash, you check it by hashing the comparable data in the same way. BCrypt hashes include the hash, the salt and the number of rounds, so there should be no problem in checking this.
The reason that the hashes are never the same is the salt will be different each time. This is to protect from rainbow table attacks.
As far as I can tell, your check is sound. The problem must be elsewhere. Are you sure that $user->password actually contains the hash in full? BCrypt hashes are 60 characters, so make sure it isn't being truncated.
Before I knew better, I implemented a login system with md5 as the hashing algorithm. Now that I do know better, I'd like to move to using PHPass. My problem is that the system is already in production and asking all users to change their passwords would be the mother of all headaches.
I've come up with a simple enough solution, but given my previous mistake I'd like to make sure I'm not making an equally grievous mistake due to ignorance.
My solution is as follows:
Change
md5($_POST['pass'])
check md5 hashed password against database value
To
md5($_POST['pass'])
pass md5 hashed password to $hasher->HashPassword()
use $hasher->CheckPassword() to check the re-hashed password against value from DB
Just for clarity, I'm only re-hashing the md5 version because that's what I already have in the DB. It's not intended as an added security measure (although if it is, that's great!).
MD5() problem is WAY exaggerated on this enthusiast programmers community site. Nothing actually bad in this hashing algorithm, especially in comparison with other parts of usual newbie application. Using phpass techniques on a usual PHP site is like using a safe lock on a paper door of a straw hut.
Most important thing in keeping passwords safe against virtual possibility of being stolen and used against the same user on other sites (oh, my!) is password strength and salt. Not hashing algorithm itself. No hashing technique would protect silly pass like "1234" or "joe".
So, md5 + strong password + average salt is better than usual password + phpass
There is not a ingle reason to phpass existing md5 hash
A sensible migration algorithm is
check this user record for the new hashing flag.
if it's set -
go for phpass auth
if not:
md5($_POST['pass'])
check md5 hashed password against database value
if correct:
phpass($_POST['pass'])
save result in the database
set new hashing flag for this record
done
The problem you're talking about isn't really specific to PHPass, but hashing passwords in general. It's basically just double-hashing. This topic has been talked about already in another question: Is "double hashing" a password less secure than just hashing it once?
If you have a look there, you can see that there is still debate over whether double hashing is worse, since it reduces the range of characters passed into the second (or subsequent) hashing function. However, it slows down the hashing process, combating brute force attacks, but only doing it twice won't act as much of a speed bump.
If you didn't want to have to deal with the double hashing, what you could try doing was adding a flag field to your users database table, and set that to true for all new users who join after you setup the new PHPass form of hashing. Then, when a user log in, if they don't have the flag field set, use the old hashing system, or the modified version you have detailed in your question. If they do have the flag field, you can use whatever new hashing process you have set up.
Update: Actually, building on that, what you could try is having that flag setup, and once they go to log in under the old system, if it is a match, then you'll still have their unhashed password in your $_POST data, so you can then run that through your new hashing setup, save the new hash, then set the flag to true, since they've been upgraded to the new hashing method. From then on, they'll be using the new hashing method.
You're getting some pretty dubious advice here, so you might want to ask on IT Security. Contrary to what some folks have said, the password hashing algorithm does matter; you want to use salting and a slow hash such as bcrypt, scrypt, or PBKDF2. See: Which password hashing method should I use?, Do any security experts recommend bcrypt for password storage?, How to securely hash passwords?, Most secure password hash algorithm(s)?. PHPass is a good library.
To migrate your users over to PHPass, you'll want to have two password hash databases: the old one (with MD5 hashes), and the new one (with PHPass hashes). Initially, the new one will be empty. When a user logs in, check whether you have an entry for them in your old password hash database (with MD5 hashes), and if you don't find an entry there, look in the new password hash database (with PHPass hashes). If you find an entry in the old database, you'll want to use MD5 to hash and check their password. If it is correct, you'll want to hash their cleartext password using PHPass, insert it into the new password hash database, and remove them from the old password hash database. If you don't find an entry in the old database, you can check for an entry in the new database and check the validity of their password with PHPass. Basically, you gradually migrate each user over from old-style to new-style hash when they next log in. Does this make sense?
My method makes sure that the password is at least 8 characters long and contains non-random garbage characters ("garbage" meaning possibly unprintable/null characters):
function pass_hash ($password) {
# take first 8 characters of the raw md5 hash
$hashslice = substr(md5($password, true), 0, 8);
# add it to the password and hash again
return md5($password . $hashslice);
}
If you don't like md5, just use sha1, the principle is the same.
I was reading this tutorial for a simple PHP login system.
In the end it recommends that you should encrypt your password using md5().
Though I know this is a beginners' tutorial, and you shouldn't put bank statements behind this login system, this got me thinking about encryption.
So I went ahead and went to (one of the most useful questions this site has for newbies): What should a developer know before building a public web site?
There it says (under security) you should:
Encrypt Hash and salt passwords rather
than storing them plain-text.
It doesn't say much more about it, no references.
So I went ahead and tried it myself:
$pass = "Trufa";
$enc = md5($pass);
echo $enc; #will echo 06cb51ce0a9893ec1d2dce07ba5ba710
And this is what got me thinking, that although I know md5() might not the strongest way to encrypt, anything that always produces the same result can be reverse engineered.
So what is the sense of encrypting something with md5() or any other method?
If a hacker gets to a password encrypted with md5(), he would just use this page!.
So now the actual questions:
How does password encryption work?
I know I have not discovered a huge web vulnerability here! :) I just want to understand the logic behind password encryption.
I'm sure I'm understanding something wrong, and would appreciate if you could help me set my though and other's (I hope) straight.
How would you have to apply password encryption so that it is actually useful?
What about this idea?
As I said, I may/am getting the whole idea wrong, but, would this method add any security in security to a real environment?
$reenc = array(
"h38an",
"n28nu",
"fw08d"
);
$pass = "Trufa";
$enc = chunk_split(md5($pass),5,$reenc[mt_rand(0,count($reenc)-1)]);
echo $enc;
As you see, I randomly added arbitrary strings ($reenc = array()) to my md5() password "making it unique". This of course is just a silly example.
I may be wrong but unless you "seed the encryption yourself" it will always be easily reversible.
The above would be my idea of "password protecting" and encrypted password, If a hacker gets to it he wont be able to decrypt it unless he gets access to the raw .php
I know this might not even make sense, but I can't figure out why this is a bad idea!
I hope I've made myself clear enough, but this is a very long question so, please ask for any clarification needed!
Thanks in advance!!
You should have an encryption like md5 or sha512. You should also have two different salts, a static salt (written by you) and then also a unique salt for that specific password.
Some sample code (e.g. registration.php):
$unique_salt = hash('md5', microtime());
$password = hash('md5', $_POST['password'].'raNdoMStAticSaltHere'.$unique_salt);
Now you have a static salt, which is valid for all your passwords, that is stored in the .php file. Then, at registration execution, you generate a unique hash for that specific password.
This all ends up with: two passwords that are spelled exactly the same, will have two different hashes. The unique hash is stored in the database along with the current id. If someone grab the database, they will have every single unique salt for every specific password. But what they don't have is your static salt, which make things a lot harder for every "hacker" out there.
This is how you check the validity of your password on login.php for example:
$user = //random username;
$querysalt = mysql_query("SELECT salt FROM password WHERE username='$user'");
while($salt = mysql_fetch_array($querysalt)) {
$password = hash('md5',
$_POST['userpassword'].'raNdoMStAticSaltHere'.$salt[salt]);
}
This is what I've used in the past. It's very powerful and secure. Myself prefer the sha512 encryption. It's actually just to put that inside the hash function instead of md5 in my example.
If you wanna be even more secure, you can store the unique salt in a completely different database.
Firstly, "hashing" (using a cryptographic one way function) is not "encrypting". In encryption, you can reverse the process (decryption). In hashing, there is (theoretically) no feasible way of reversing the process.
A hash is some function f such that v cannot be determined from f(v) easily.
The point of using hashing for authentication is that you (or someone seeing the hash value) do not have any feasible way (again, theoretically) of knowing the password. However, you can still verify that the user knows his password. (Basically, the user proves that he knows v such that f(v) is the stored hash).
The weakness of simply hashing (aside from weak hash functions) is that people can compile tables of passwords and their corresponding hash and use them to (effectively) get the inverse of the hash function. Salting prevents this because then a part of the input value to the hash is controlled and so tables have to be compiled for that particular salt.
So practically, you store a salt and a hash value, and authenticate by hashing a combination of the salt and the password and comparing that with your hash value.
MD5 is a one way hashing function which will guard your original password more or less safely.
So, let's say your password is "Trufa", and its hashed version is 06cb51ce0a9893ec1d2dce07ba5ba710.
For example, when you sign in to a new webpage, they ask you for your username and password. When you write "Trufa" as your password, the value 06cb51ce0a9893ec1d2dce07ba5ba710 is stored in the database because it is hashed.
The next time you log in, and you write "Trufa", the hashed value will be compared to the one in the database. If they are the same, you are authenticated! Providing you entered the right username, of course.
If your password wasn't stored in its hashed form in database, some malicious person might run a query somehow on that database and see all real passwords. And that would be compromising.
Also, since MD5 is a 128 bit cryptographic function, there are 2^128-1 = 340282366920938463463374607431768211455 possible combinations.
Since there are more possible strings than this, it is possible that 2 strings will generate the same hash value. This is called a collision. And it makes sure that a hashed password cannot be uniquely reverse engineered.
The only vulnerability with salting is that you need to know what the salt is in order to reconstruct the hash for testing the password. This is gotten around by storing the entry in the authdb in the form <algorithm>$<salt>$<hash>. This way the authdb entry can be used by any code that has access to it.
You're missing the important step - the salt. This is a unique (per user, ideally) bit of extra data that you add to the password before hashing it.
http://en.wikipedia.org/wiki/Salt_%28cryptography%29
Your idea (salting) is well known and is actually well-implemented in the PHP language. If you use the crypt() function it allows you to specify a string to hash, a method to encrypt (in some cases), and a salt. For example,
$x = crypt('insecure_password', $salt);
Returns a hashed and salted password ready for storage. Passwords get cracked the same way that we check if they're right: we check the hash of what the user inputs against the hash of their password in the database. If they match, they're authenticated (AFAIK this is the most common way to do this, if not the only). Insecure passwords (like password) that use dictionary words can be cracked by comparing their hash to hashes of common passwords. Secure passwords cannot be cracked this way, but can still be cracked. Adding a salt to the password makes it much more difficult to crack: since the hacker most likely doesn't know what the salt is, his dictionary attack won't work.
For a decent hash the attacker won't be reversing the hash, they'll be using a rainbow table, which is essentially a brute-force method made useful if everyone uses the same hash function.
The idea of a rainbow table is that since hashing is fast I can hash every possible value you could use as a password, store the result, and have a map of which hash connects to which password. If everyone just takes their passwords and hashes them with MD5 then my hash table is good for any set of password hashes I can get my hands on!
This is where salting comes in. If I take the password the user enters and add some data which is different for every user, then that list of pre-determined hashes is useless since the hash is of both the password and some random data. The data for the salt could be stored right beside the password and even if I get both it doesn't help me get the password back since I still have to essentially brute force the hash separately for every single user - I can't form a single rainbow table to attack all the hashes at once.
Of course, ideally an attacker won't get the list of hashed passwords in the first place, but some employees will have access so it's not possible to secure the password database entirely.
In addition to providing salt (or seed), the md5 is a complex hashing algorithm which uses mathematical rules to produce a result that is specifically not reversable because of the mathematical changes and dataloss in throughput.
http://en.wikipedia.org/wiki/Cryptographic_hash_function
md5 (or better put: hash algorithms in general) are used to safely store passwords in database. The most important thing to know about hashes is: Hashes are not encryptions per se. (they are one-way-encryptions at most). If you encrypt something, you can get the data back with the key you used. A hash generates a fixed-length value from an arbitrary input (like a string), which can be used to see if the same input was used.
Hashes are used to store sensitive, repeatly entered data in a storage device. Doing this, nobody can recreate the original input from the hash data, but you can hash an incoming password and compare it to the value in the database, and see if both are the same, if so, the password was correct.
You already pointed out, that there possibilites to break the algorithm, either by using a database of value/hash pairs or producing collisions (different values resulting in the hash value). You can obscure this a bit by using a salt, thus modifying the algorithm. But if the salt is known, it can be used to break the algorithm again.
I like this question. But I think you've really answered yourself.
The site you referenced uses dictionary lookups of known, unsalted, md5's - it doesn't "crack" anything.
Your example is almost good, except your application needs to be able to regenerate the md5 using the same salt every time.
Your example appears to use one of the random salts, which will fail 2 of 3 times if you try to compare a users password hash to something input.
People will tell you to also use SHA1 or SHA256 to be have a 'stronger' hash - but people will also argue that they're all 'broken.'
That documentation is misleading -- it teaches a "vulnerable" concept and presents it as somehow being "secure" because it (the saved password) looks like gibberish. Just internet junk that won't die. The following link should clear things up (you have already found a good bit of it though, it seems. Good work.)
Enough With The Rainbow Tables: What You Need To Know About Secure Password Schemes talks about MD5 (and why it should not be used) along with salt (e.g. how to thwart rainbow attacks) as well as provides useful insights (such as "Use someone else’s password system. Don’t build your own"). It is a fairly good overview.
This is my question about the aspects of md5 collision, slightly related to your question:
Is there any difference between md5 and sha1 in this situation?
The important part is in the first 3 rows, that is: you must put your salt before the password, if you want to achieve stronger protection, not after.
To simply answer the title of your question, md5's only real use nowadays is for hashing large strings (such as files) to produce checksums. These are typically used to see if both strings are identical (in terms of files, checksums are frequently used for security purposes to ensure a file being distributed hasn't been tampered with, for example).
To address each of your inline questions:
How does password encryption work?
How would you have to apply password encryption so that it is actually useful?
Secure password hashing works by taking the password in plain text form, and then applying a costly hashing function to it, salted with a cryptographically secure random salt to it. See the Secure hash and salt for PHP passwords question for more detail on this.
What about this idea?
Password hashing does not need to be complicated like that, and nor should it be. Avoid thinking up your own algorithms and stick with the tried and tested hashing algorithms already out there. As the question linked above mentions, md5() for password hashing has been obsolete for many years now, and so it should be avoided.
Your method of generating a "random" salt from an array of three different salts is not the randomness you're looking for. You need unique randomness that is suitable for cryptographically secure (i.e. using a cryptically secure pseudo-random number generator (CSPRNG)). If you're using PHP 7 and above, then the random_bytes function can be used to generate a cryptographically secure salt (for PHP 5 users, the random_compat library can be used).
I am connecting to a MySQL database with PHP and the CodeIgniter Framework. I want to store my passwords encrypted in the database and would like to know the best way to do this.
From a high level overview - don't encrypt, hash. And if you can, use BCrypt. Here's a long article explaining why BCrypt and why hashing.
Encrypting the passwords is a bad idea. If somebody gets your database, they're probably going to get the key you used to encrypt the passwords as well.
The real solution is to hash, salt, and then store the passwords. Jeff Atwood has an awesome post on this:
http://www.codinghorror.com/blog/archives/000953.html
And here is one discussing "rainbow tables," massive tables of words with their MD5 sums:
http://www.codinghorror.com/blog/archives/000949.html
The best way, in that it is both easy and secure, is to use phpass. If your PHP installation does Blowfish, it uses bcrypt; if it doesn't, it uses multiple passes of md5. Either way, it's more secure than straight md5 or sha1.
$hasher = new PasswordHash(8, false);
// Before storing a password
$hash = $hasher->HashPassword($password);
// To check a password against a hash
if ($hasher->CheckPassword($password, $hash))
// $password and $hash match
I always md5sum passwords before I put them into the database, and then also md5sum password login attempts to check them against the db. Just for safety, I do a select query using a where clause with userID (username) AND md5summed password so that I don't get any rows back at all unless both match.
Also, mysql interanlly uses md5summing on it's passwords if you need a level of trust in this method of password obfuscation.
Use a hashing function; MD5 will do fine. Don't store the password, store the hash. Then, to log users in, hash the password they entered and compare it to the hash in the database; if the hashes match, they're legit.
Also, add a salt to the password before you hash it. This can be easy as just appending a string to the password, like your domain name or some other data that's unique to your app. This prevents hackers from using rainbow tables against your hashes.
Never store passwords. Use a one way hash and store that.
hmm, I hash, more than once based on whatever math springs to mind at the time of writing the storing and validation of passwords
From here on I'll probably go with OpenID as much as possible wherever I have an actual choice tho, so i don't have to do any password storage at all. That way I can leave passwords up to the experts, and the users already trusted party.
I agree with hashing and salting. I disagree with picking a site-wide hash. Use a different salt for each user to prevent dictionary attacks against all your passwords at once.