Different passwords, same hash check using crypt in php - php

If i use crypt() to hash a password:
$password = "my_password_12345";
$salt = base64_encode(openssl_random_pseudo_bytes(64, $cstrong));
$crypt = crypt($password, $salt);
I get something like this
echo $crypt; //AG6hHvhjwnqpc
So, when I check for the hash I do this and all work fine
echo crypt($password, $crypt); //AG6hHvhjwnqpc
But why the following happens? I do the same check as above but with a password similar to the previous one and I get the same hash.
$password = "my_password_12345_not!";
echo crypt($password, $crypt); //AG6hHvhjwnqpc
I would expect a different hash, but instead I'm getting the same

In PHP, the crypt function use only the 8 first characters :
Extract from the documentation:
The standard DES-based crypt() returns the salt as the first two characters of the output. It also only uses the first eight characters of str, so longer strings that start with the same eight characters will generate the same result (when the same salt is used).

Related

How to match username with encrypted password on login [duplicate]

This question already has an answer here:
How to check a mysql encrypt value with a salt in PHP?
(1 answer)
Closed 9 years ago.
I would like to encrypt some passwords and put it in database. How do I keep this stuff in a database so I can retrieve the data if the owner matches.
Example
<?php
// some validations and other staff
$data = $_POST['input'];
$hash = crypt($data);
//then database insert code
?>
If I echo the $hash, it's giving me some encrypted data but when I refresh the page, the numbers are changing from time to time. How do I keep the data static? How will I tell the encrypted password that this was the owner when username and password entered.
Example
<?php
//time of encryption
$name = "someone";
$pass = "p1x6Fui0p>j";
$hash = "$pass"; //outcome of $hash e.g. $1$aD2.bo0.$S93XNfgOFLskhis0qjE.Q/
// $hash and $name inserted in database
?>
When the user tries to login with collect details, how will I refer $hash "$1$aD2.bo0.$S93XNfgOFLskhis0qjE.Q/" was equal to $pass "p1x6Fui0p>j" ?
crypt() has an unfortunate name. It's not an encryption function, but a one-way hashing function.
If you're using PHP 5.5+, just use password_hash and password_verify:
$hash = password_hash($data, PASSWORD_BCRYPT); // Bcrypt is slow, which is good
And to verify the entered password:
if (password_verify($pass, $hash)) {
// The password is correct
}
Now to answer your actual question: the purpose of password hashing is to authenticate users without actually storing their plaintext passwords. If hash(a) == hash(b), then you can be pretty sure that a == b. In your case, you already have hash(a) ($hash), so you just need to hash the inputted password and compare the resulting hashes.
crypt() does this for you:
if (crypt($pass, $hash) === $hash) {
// The password is correct
}
From the php crypt page
if (crypt($user_input, $hashed_password) == $hashed_password) {
echo "Password verified!";
}
You are not using your own salt, so for every call salt is automatically generated, and salted password is hashed. To get the same hash from this password, you need to run crypt with exact salt that was generated during first run.
Generated salt varies depending on algorithm used for hashing, but from your example it's MD5, and salt is delimited by first and third dollar sign inclusively:
$hash = '$1$aD2.bo0.$S93XNfgOFLskhis0qjE.Q/';
// \ salt /
So to get Exact same hash you need to call crypt($pass, '$1$aD2.bo0.$');
Remember that if you want to use your own salt, it needs to be in proper format for given algorithm. For best results use php 5.5+ password_hash mentioned by #Blender, and for older php versions there is password_compat library, with this you don't have to worry about proper salt format.

Blowfish and Crypt Successful Example?

I'm reading up on protecting passwords and want to get feedback on if my code is correct. I'm trying to use blowfish and crypt() together to prevent anyone from decrypting passwords. Also, I have a question. When I store the password, I assume I will have to also store the variable $string so that I can use it again to verify the user when he signs in, correct?
function unique_md5() {
mt_srand(microtime(true)*100000 + memory_get_usage(true));
return md5(uniqid(mt_rand(), true));
}
//unique_md5 returns a random 16 character string
$string = '$2a$07$' . unique_md5();
$password = 'password';
$password = trim($password);
$protected_password = crypt($password, $string);
//then store the variables $string and $protected_password into the database
Blowfish takes a 22 character string for it's salt (that's not including the type and the cost parameter). MD5 returns a 32 character string, which is not accepted by crypt_blowfish. Use proper salts.
No, you don't need to store the salt with the encrypted password, as the encrypted password is returned to you with the salt already prepended. When you verify if a plaintext password matches a crypted password, you only need to check if $crypted_password == crypt($plaintext_password, $crypted_password).

PHP Crypt not working Ubuntu PHP 5.3.6

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

How do I effectively use crypt()

I don't understand the documentation at php.net. It appears they are using the encrypted version of password as the salt when testing against the original encryption.
When I insert crypt with out the optional second parameter (the salt) I get different encrypted versions of the same password. Is this expected behavior?
However if I insert a second parameter of 'd4' then I get the same encrypted passwords for the same password input. Expected behavior.
Prior to insertion on signup:
$pass = crypt('$pass', 'd4'); // after this I insert $pass into the mysql table
Testing on signin:
$pass = crypt($pass, 'd4'); // after this I test $pass against the mysql table
PHP.net documentation:
<?php
$password = crypt('mypassword'); // let the salt be automatically generated
/* You should pass the entire results of crypt() as the salt for comparing a
password, to avoid problems when different hashing algorithms are used. (As
it says above, standard DES-based password hashing uses a 2-character salt,
but MD5-based hashing uses 12.) */
if (crypt($user_input, $password) == $password) {
echo "Password verified!";
}
?>
How does this work?
Since crypt() only uses the first two characters (or whatever CRYPT_SALT_LENGTH is) of the salt argument, passing in the encrypted password (of which the first characters are the salt originally used to encrypt it) does the right thing.
If no salt argument is passed in, a random salt is generated and used.
If your question is... Is it normal that with certain encryption ciphers you are returned with different encrypted strings for the same password/input? The answer is yes. Not to sure what you are refering to about salt. Salt it just salt. In the end it is a deturant and means nothing. Using passwords or encrypted forms of passwords as salt is not recommended, but using some random hash (base64) of a phrase is commonly used. Let me know if this doesn't any, and I'll try again.

Comparing passwords with crypt() in PHP

I need to get the basics of this function. The php.net documentation states, for the blowfish algorithm, that:
Blowfish hashing with a salt as follows: "$2a$", a two digit cost parameter, "$", and 22 base 64 digits from the alphabet "./0-9A-Za-z". Using characters outside of this range in the salt will cause crypt() to return a zero-length string
So this, by definition, should not work:
echo crypt('rasmuslerdorf', '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringforsalt$');
However, it spits out:
$2a$07$usesomadasdsadsadsadaeMTUHlZEItvtV00u0.kb7qhDlC0Kou9e
Where it seems that crypt() has cut the salt itself to a length of 22. Could somebody please explain this?
Another aspect of this function I can't get my head around is when they use crypt() to compare passwords. http://php.net/manual/en/function.crypt.php (look at ex. #1). Does this mean that if I use the same salt for all encrypting all my passwords, I have to crypt it first? ie:
$salt = "usesomadasdsadsadsadae";
$salt_crypt = crypt($salt);
if (crypt($user_input, $salt) == $password) {
// FAIL WONT WORK
}
if (crypt($user_input, $salt_crypt) == $password) {
// I HAVE TO DO THIS?
}
Thanks for your time
Following code example may answer your questions.
To generate hashed password using Blowfish, you first need to generate a salt, which starts with $2a$ followed by iteration count and 22 characters of Base64 string.
$salt = '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringfors';
$digest = crypt('rasmuslerdorf', $salt);
Store the whole $digest in database, it has both the salt and digest.
When comparing password, just do this,
if (crypt($user_input, $digest) == $digest)
You are reusing the digest as salt. crypt knows how long is the salt from the algorithm identifier.
New salt for every password
$password = 'p#ssw0rd';
$salt = uniqid('', true);
$algo = '6'; // CRYPT_SHA512
$rounds = '5042';
$cryptSalt = '$'.$algo.'$rounds='.$rounds.'$'.$salt;
$hashedPassword = crypt($password, $cryptSalt);
// Store complete $hashedPassword in DB
echo "<hr>$password<hr>$algo<hr>$rounds<hr>$cryptSalt<hr>$hashedPassword";
Authentication
if (crypt($passwordFromPost, $hashedPasswordInDb) == $hashedPasswordInDb) {
// Authenticated
Quoting from the manual
CRYPT_BLOWFISH - Blowfish hashing with
a salt as follows: "$2a$", a two digit
cost parameter, "$", and 22 base 64
digits from the alphabet
Note: 22 base 64 digits
BCrypt uses 128 bits for salt, so 22 bytes Base64, with only two bits of the last byte being used.
The hash is computed using the salt and the password. When you pass the crypted password, the algorithm reads the strength, the salt (ignoring everything beyond it), and the password you gave, and computes the hash, appending it. If you have PostgreSQL and pg_crypto handy, SELECT gen_salt('bf'); will show you what of $salt is being read.
Here's a code sample for salt generation, from my .NET implementation's test-vector-gen.php, alternatively:
$salt = sprintf('$2a$%02d$%s', [strength goes here],
strtr(str_replace(
'=', '', base64_encode(openssl_random_pseudo_bytes(16))
),
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
'./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'));
There is no reason to use the same salt for all of your passwords. The salt is part of the output anyway so you gain nothing in convenience... though I grant PHP ought to have a built-in gen_salt function.
First question:
So this, by definition, should not work:
echo crypt('rasmuslerdorf', '$2a$07$usesomadasdsadsadsadasdasdasdsadesillystringforsalt$');
Where it seems that crypt() has cut
the salt itself to a length of 22.
Could somebody please explain this?
There isn't a problem with having too many characters... the phrase Using characters outside of this range in the salt will cause crypt() to return a zero-length string referse to outside the range of base 64 not the range of 22 characters. Try putting an illegal character in the salt string, and you should find that you get an empty output (or if you put < 22 characters in, resulting in illegal empty bytes).
Second question:
You pass in the encrypted stored password as salt because the salt string always appears (by design) in the encrypted string, and this way you ensure that you have the same salt for both encryption of stored and user-entered password.
This question is in relation to my response to ZZ Coder's answer. Basically my question is regarding storing the crypt() result in the database. Am I supposed to store the entire output in the database, so that my database looks like this:
--------------------------------------------------------------------------------
| ID | Username | Password |
--------------------------------------------------------------------------------
| 32 | testuser | $2a$07$usesomadasdsadsadsadaeMTUHlZEItvtV00u0.kb7qhDlC0Kou9e |
--------------------------------------------------------------------------------
If yes, then doesn't this kind of defy the purpose of using a salt in the first place? If someone gains access to the db, they can clearly see the salt used for the encryption?
Bonus question: Is it secure to use the same salt for every password?

Categories