I am about to use this for a user password system but I first want to make sure I'm doing it correctly. Here is my test code:
function generateBlowfishSalt() {
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./';
$numChars = strlen($chars);
$salt = '';
for($i = 0; $i < 22; ++$i) {
$salt .= $chars[mt_rand(0, $numChars - 1)];
}
return $salt;
}
$password = 'foo';
$salt = generateBlowfishSalt();
$hash1 = crypt($password, '$2a$12$' . $salt);
$hash2 = crypt($password, $hash1);
$compare = ($hash1 == $hash2);
printf('password: %s</br>', $password);
printf('salt: %s</br>', $salt);
printf('hash1: %s</br>', $hash1);
printf('hash2: %s</br>', $hash2);
printf('compare: ' . $compare);
Here is a sample output:
password: foo
salt: MYVJ32OqLcMGBar3pUa.0S
hash1: $2a$12$MYVJ32OqLcMGBar3pUa.0OTRwv6UX0bcxnSmheKOcqjvqvCrM/p2q
hash2: $2a$12$MYVJ32OqLcMGBar3pUa.0OTRwv6UX0bcxnSmheKOcqjvqvCrM/p2q
compare: 1
My main questions are:
Am I generating the 22 character salt correctly? Some implementations I've seen use base64_encode() to generate the salt. Do I need to do that?
Is it correct to store the whole $hash1 value in the database and not store a separate salt value since $hash1 will have the salt in it and that's all I need to verify the password?
On the PHP crypt() document page the CRYPT_BLOWFISH example has a trailing $ for the salt argument(it's this: '$2a$07$usesomesillystringforsalt$'). But in all the examples I've seen no one uses the trailing $. Is it optional?
Thanks
You are doing the salt correctly. Just has to be random. There are many different methods for random generation of this. Some people use it in addition to date times, etc. Also, longer salts do not mean much as it will be cut off anyways. It requires a certain length, at least in terms of crypt(). So if a little extra padding makes you feel better, go for it.
Your suppose to store the salt in the database. I had trouble understanding that at first. The whole point of a salt is only to make it take longer to rainbow table the passwords with a huge list of possible passwords. Correction, in addition, it also helps with 2 or more passwords that are the same, which is bound to happen. If so, the hashes are still going to be different due to the random salts.
As for crypt() keep testing it until it looks and is the same length as the one on the php DOC for PHP, but yes it looks correct.
Related
This is my password encrypting code :
// Create a 256 bit (64 characters) long random salt
// Let's add 'something random' and the username
// to the salt as well for added security
$salt = hash('sha256', uniqid(mt_rand(), true) . 'something random' . strtolower($username));
// Prefix the password with the salt
$hash = $salt . $password;
// Hash the salted password a bunch of times
for ( $i = 0; $i < 100000; $i ++ )
{
$hash = hash('sha256', $hash);
}
// Prefix the hash with the salt so we can find it back later
$hash = $salt . $hash;
I lost the tutorial site. Do anyone know how to decrypt this encryption. Thank you very much. Appreciate your help
There is no *de*cryption algorithm because there's no *en*cryption algorithm. What you're doing is a hash, which is a non-reversible operation. And that's exactly the point, you do not want to store anything that would even allow you the chance of knowing what the actual secret password is.
A hashing function is not the same thing as encryption. Check the Wiki on hashing. Bottom line is: a hash is a one way algorithm. You can't decrypt it in one go. You could brute-force it, but (especially with sha256) that would take ages. If you were to have a machine, dedicated to cracking a sha256 hash, it'd take ~= 10^64 years!. If 10^64 is meaningless, here's the number in full:
100.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000.000
And even then, there's no guarantee the result will be right: you could end up with a hash collision (google it). If you do: cheer up, you'd be the first, AFAIK.
For more on encryption vs hashing, refer to this answer to a previous SO question
So the answer is: You can't decrypt (or rather de-hash) what you have.
I have this code in a PHP page which I am running multiple times. Every time I refresh the page, the salt changes (as it should), but the hash output stays the same.
$iv = mcrypt_create_iv(22);
$ro = rand(6, 9);
$salt = '$2y$'.$ro.'$'.$iv.'$';
echo $salt;
echo '<br />';
$crypt = crypt('test', $salt);
echo $crypt;
Shouldn't the random salt affect the output and make it so that every time I refresh the page the crypt result changes too?
I also have a few general questions on crypt().
Is there any way for you to use a specific hashing algorithm with this function? I would like to use the blowfish algorithm.
Is it the salt length/format that affects which algorithm it chooses?
Lastly, should the salt length for the blowfish algorithm always be 22 characters, or is that just the maximum?
By the way, if anyone is wondering (and if it matters for answering these questions and wasn't obvious), I'm planning to use something similar to store hashed passwords.
Thank you for looking!
The crypt() function on your system does not support the "2y" algorithm. At least Linux GLIBC 2.7 based systems only know DES, $2a$ (blowfish), $5$ (SHA-256) and $6$ (SHA-512). Therefore, the crypt() function assumed DES and only took the first two characters "$2" as salt. That of course produced always the same output.
Try SHA-512 for secure password hashes:
$salt_chars = array_merge(range('A','Z'), range('a','z'), range(0,9));
$salt = '$6$';
for($i=0; $i < 8; $i++) { $salt .= $salt_chars[array_rand($salt_chars)]; }
echo "salt=$salt\n";
$crypt = crypt('test', $salt);
echo "crypt=$crypt\n";
Regarding your second question, you can chose the algorithm by starting the salt with e.g. "$2a$" (instead the $6$ above) for blowfish. Read "man 2 crypt" for details. For some algorithms you can also encode more parameters like "rounds=4000" into the salt.
According the the crypt() manpage, the salt may be up to 16 characters following the $id$.
Longer salts will silently be truncated and produce the same output as for only the first 16 characters.
BTW, even in /etc/shadow, passwords only use 8 characters of salt with the SHA-512 algorithm. As the salt is only to make rainbow table attacks harder, this seems sufficient.
I'm upgrading my auth class, replacing md5 with crypt for storing passwords. Here's the approach I've taken:
function crypt_pass($pass, $userID) {
$salt = $userID .'usesomesillystringforsalt'; // min 22 alphanumerics, dynamic
$method = (version_compare('5.3.7',PHP_VERSION,'>=')) ? '2y' : '2a'; // PHP 5.3.7 fixed stuff
if (CRYPT_BLOWFISH == 1) {
$blowfish_salt = '$'. $method .'$07$'. substr($salt, 0, CRYPT_SALT_LENGTH) .'$';
return crypt($pass, $blowfish_salt);
}
return sha1($pass . $salt);
}
Making the salt unique per user adds a step, a db lookup for the supplied username's id ... I figure it's worth it. Am I wrong about that? Is there anything else I'm not considering here?
The whole point of salting is to produce different encrypted strings from the same password. If you use the same salt every time, this will not happen, so you may as well not use salts at all. You should be creating a random new salt for every password, and then storing it in the database alongside the encrypted-and-salted string.
Why do the crypt values not match on Ubuntu PHP 5.3.6? On other systems, they match.
Sample code:
<?php
$password = '12345';
$saltString = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
$salt = '_';
while (strlen($salt) < 9)
$salt .= substr($saltString, rand(0, strlen($saltString)-1), 1);
$cryptedPassword = crypt($password, $salt);
printf("Password: %s\n", $password);
printf("Crypted Password: %s\n", $cryptedPassword);
$cryptCompare = crypt($password, $cryptedPassword);
printf("Crypted Password Comparison: %s\n", $cryptCompare);
?>
Password: 12345
Crypted Password: _8OixMoOTyONAZDOiHbs
Crypted Password Comparison: _8IK4dGYmlkVo
I believe that crypt is supposed to return the salt value prepended to the front of the return value. In some implementations it is apparently only 2 bytes (you can check it with the constant CRYPT_SALT_LENGTH). From looking at the output printed in the OP, the similarity in the two "encrypted" strings is limited to the first two bytes. Perhaps the implementation is flawed and uses more than two bytes for the salt but only returns the first two bytes of the salt in the result. If so, that would explain the difference. You could test that by simply setting the salt length at 2.
Having said that, you might want to consider using a different hashing function. I know very little about PHP, but a bit of googling seems to indicate that crypt is obsolete and not very secure. For example, this is one such post.
Perhaps your system doesn't support your current hash type. Why not try a different hash type?
http://php.net/manual/en/function.crypt.php
I've been using PHP's crypt() as a way to store and verify passwords in my database. I use hashing for other things, but crypt() for passwords. The documentation isn't that good and there seems to be a lot of debate. I'm using blowfish and two salts to crypt a password and store it in the database. Before I would store the salt and the encrypted password, (like a salted hash) but realized its redundant because the salt is part of the encrypted password string.
I'm a little confused on how rainbow table attacks would work on crypt(), anyway does this look correct from a security standpoint. I use a second salt to append to the password to increase the entropy of short passwords, probably overkill but why not?
function crypt_password($password) {
if ($password) {
//find the longest valid salt allowed by server
$max_salt = CRYPT_SALT_LENGTH;
//blowfish hashing with a salt as follows: "$2a$", a two digit cost parameter, "$", and 22 base 64
$blowfish = '$2a$10$';
//get the longest salt, could set to 22 crypt ignores extra data
$salt = get_salt ( $max_salt );
//get a second salt to strengthen password
$salt2 = get_salt ( 30 ); //set to whatever
//append salt2 data to the password, and crypt using salt, results in a 60 char output
$crypt_pass = crypt ( $password . $salt2, $blowfish . $salt );
//insert crypt pass along with salt2 into database.
$sql = "insert into database....";
return true;
}
}
function get_salt($length) {
$options = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789./';
$salt = '';
for($i = 0; $i <= $length; $i ++) {
$options = str_shuffle ( $options );
$salt .= $options [rand ( 0, 63 )];
}
return $salt;
}
function verify_password($input_password)
{
if($input_password)
{
//get stored crypt pass,and salt2 from the database
$stored_password = 'somethingfromdatabase';
$stored_salt2 = 'somethingelsefromdatabase';
//compare the crypt of input+stored_salt2 to the stored crypt password
if (crypt($input_password . $stored_salt2, $stored_password) == $stored_password) {
//authenticated
return true;
}
else return false;
}
else return false;
}
You really should have a look at PHPASS: http://www.openwall.com/phpass/ It's a password hashing framework using crypt() which is used in projects like Wordpress and phpBB.
There is also an excellent article on this website about password hashing, salting and stretching using crypt(): http://www.openwall.com/articles/PHP-Users-Passwords
UPDATE:
Currently there's an alternative for the PHPASS library. In the next version of PHP there are special functions for hashing and verifying passwords (using bcrypt): http://www.php.net/manual/en/ref.password.php. There is a compatibility library that implements these functions for PHP 5.3.7+: https://github.com/ircmaxell/password_compat
Your use of crypt() is fine. crypt($input, $stored) == $stored is the way it is designed to be used.
Your get_salt() function is not great, since it is using the often-poor rand() function. You should consider using a stronger random function, like openssl_random_pseudo_bytes(), instead.
The idea of a rainbow table is that an attacker can make a table with all possible passwords and their hashes at home.
E.g.
PASSWORD HASH
iloveSO gjroewjgo
password knbnogjwm
secret gjroehghe
jbieber rewgroewj
etc.
With this table, the attacker can quickly convert any hash to a password. Rainbow table uses some tricks so that not all hashes have to be stored, but it still computes all hashes beforehand.
By using a salt, even when storing it with the password, you make this much harder. Instead of hashing every word in a dictionary, the attacker would now have to hash every word with every salt. With a long enough salt, this gives enough combinations to make it unfeasible to compute all these hashes.
So a salt is not meant to be an extra password, known only to the application, it is meant to change the hash function so that it is non-standard.
This is a misuse of crypt() because you are using a deprecated primitive. Blowfish is very old, twofish is the replacement and even that is old because threefish is almost finalized. You should be using a member of the sha2 family, sha256 or sha512 are both good choices. crypt() can be used with sha256 or sha512, you should use the CRYPT_SHA256 CRYPT_SHA512 parameters respectively.
Also your salts have a very small entropy/size ratio, you are only using an alphanumeric set which is a joke because alphanumeric rainbow tables are the most common. You should be using a full byte which base256, and I recommend a salt that is 256 bytes long. Keep in mind all hash functions are binary safe by definition thus you shouldn't have to worry about null bytes and the like.
Use SHA-512 (if available) with a salt which includes time() and openssl_random_pseudo_bytes(). Crypt is consolidated / efficient because it returns the salt inserted with the hashed string.