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.
Related
When we use CRYPT() method of any variable.
$test = 'password';
echo CRYPT($test);
Result
$1$g9s9ZdtF$sBBiBc4PdljOL4sDLx4CK.
When we use MD5() method of same variable.
$test = 'password';
echo MD5($test);
Result
5f4dcc3b5aa765d61d8327deb882cf99
Now, what is difference in both Answer?
crypt() is a function that creates password hashes from plaintext and a (randomly generated or provided) salt value. It can use several underlaying hash algorithms like DES, MD5, Blowfish or SHA.
Using a "salted" password means that the same plaintext input does not always produce the same hash. So you can't say "I've seen this hash before it's the one for 'abc123'".
As it's available in libc there are crypt() functions in almost all programming languages and database servers so its very interoperable.
How secure it is only depends on the hash algorithm you chose (specified in as part of the hash).
For more information see https://en.wikipedia.org/wiki/Crypt_(C)
im currently trying to understand hashes and salts. As i understand it, it should not be possible to verify a password, if i only have the password and the generated hash(that was generated with a random salt ofc).
So how can the password_verify function in PHP verify my password, if i dont give it the salt? Is there a hidden variable in the background, that stores it for the php hashing functions?
And if that is the case, how can
doveadm pw -t '{SHA512-CRYPT}$6$myhash...' -p "qwertz"
verify it too, even if i run it on a complety different computer? Thats a tool, that comes with Dovecot(a MDA).
Here is my PHP code, that creates a random salt with 64 chars, combines it with a password, creates a hash and verifies the hash via password_verify().
I just started working on the whole hash/salt/pepper thing today, so there could be a huge flaw in my whole train of thought.
<?php
$password = "qwertz";
$salt = createSalt(64);
$hash = crypt($password, "$6$$salt");
if (password_verify($password, $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
function createSalt($length){
$chars = "IrhsYyLofUKj4caz0FDBCe2W9NRunTgQvp7qOXmS5GM3EJV6i8tAHdkPbxwl1Z";
$salt="";
for($i=0; $i < $length; $i++){
$newchar = substr($chars, rand(0,strlen($chars)-1),1);
$salt .= $newchar;
}
return $salt;
}
?>
The hash contains several pieces of information. This article explains the format used by Unix but I believe PHP password functions use a similar format (if not the same):
The hash field itself is comprised of three different fields. They are
separated by '$' and represent:
Some characters which represents the cryptographic hashing mechanism used to generate the actual hash
A randomly generated salt to safeguard against rainbow table attacks
The hash which results from joining the users password with the stored salt and running it through the hashing mechanism specified in
the first field
It can also include the exact per-algorithm options used to generate the hash, such us the algorithmic cost:
var_dump(password_hash('foo', PASSWORD_BCRYPT, [
'cost' => 8,
]));
string(60) "$2y$08$7Z5bTz7xXnom8QsrbZ7uQetMLxOZ7WjuDkUYRIh73Ffa17GV1Tb7q"
Here $2y$08$ means that Bcrypt with cost 8 was used.
If we use the newer Argon2 available in PHP/7.2 there're even more params:
$argon2i$v=19$m=1024,t=2,p=2$YzJBSzV4TUhkMzc3d3laeg$zqU/1IN0/AogfP4cmSJI1vc8lpXRW9/S0sYY2i2jHT0
Some backgrounds to the answer from #Álvaro González :
PHP manual suggests using "password_hash" instead of "crypt" function through "password_hash" is a "crypt()" wrapper ( Because, it uses a strong hash, generates a strong salt, and applies proper rounds automatically. )
"password_hash()" returns the algorithm, cost, and salt as part of the returned hash. Therefore, all information that's needed to verify the hash is included in it. This allows the "password_verify" function to verify the hash without needing separate storage for the salt or algorithm information. : http://php.net/manual/en/function.password-verify.php
Since, "password_hash" is a wrapper for "crypt", "crypt" also does the same, ie., returns the algorithm, cost, and salt as part of the returned hash. and thus "password_verify" can verify the hash.
Now, please check the answer given by #Álvaro González
I've used the following instructions to install a mail server:
http://www.geoffstratton.com/ubuntu-mail-server-postfix-dovecot-and-mysql
Now I'm trying to program a login form in PHP but don't know how to compare the entered password with the saved password.
This is the mysql-code for the password encryption:
ENCRYPT('PASSWORD', CONCAT('$6$', SUBSTRING(SHA(RAND()), -16)))
I don't understand how it works because with every call of this function a completely new string is being generated.
This is what I have so far:
crypt($_POST[‘password’], '$6$'.substr(sha1(rand()), 0, 16))
But as I said every time I get a new string.
Use the PHP functions password_hash and password_verify.
These functions salt and iterate to provide secure protection.
See PHP Manual password_hash and password-verify.
string password_hash ( string $password , integer $algo [, array $options ] )
Returns the hashed password, or FALSE on failure.
boolean password_verify ( string $password , string $hash )
Returns TRUE if the password and hash match, or FALSE otherwise.
Example code:
$hash = password_hash("rasmuslerdorf", PASSWORD_DEFAULT)
if (password_verify('rasmuslerdorf', $hash)) {
echo 'Password is valid!';
} else {
echo 'Invalid password.';
}
In your case you grab the password hash for that username from the database, and keep it in a variable called $hash. Then you use password_verify() like this:
password_verify($_POST["password"], $hash)
Consider the case where a password is simply stored as its hash. Anyone reading the data would have a hard job working out what the password actually is, however its not impossible, indeed there are online database containg vast numbers of indexed hashes and the corresponding cleartext - its possible to simply lookup the hash for commonly chosen passwords. Further, consider the case if two users had the same hash - that also means anyone reading the data would know they had the same password.
Both these problems are addressed on secure system by adding a random string, known as a salt to the cleartext. This salt is only generated once and is then stored alongside the password.
So the data you have stored is $6$[salt]$[hash of (salt + password)]
To verify the password you recreate the hash using the stored salt and the password presented and compare that with stored hash. The crypt function ignores any data after the salt, so you simply do this:
if ($stored === crypt($_REQUEST['password'], $stored)) {
// Password is valid
The code you are using has very low entropy in its salt derivation - this is probably adequate for most purposes but not in highly secure contexts.
I modified my old post. I tried the crypt() function and now trying to work with password_hash() and password_verify() to verify the encrypted password coming from database but on each call, password_hash() function retuns a different encrypted string and password_verify() cannot match it.
This is how I am doing this.
//please ignore the syntax error if any
$data = '11';
$dbpass = password_hash($data, PASSWORD_BCRYPT);
echo $dbpass; // displays the random strings on each page refresh.
Once password is saved into database does not get match during the login process. Below is my actual function.
private function process_data($password){
$password = __STR.$password.__STR;
return password_hash($password, PASSWORD_BCRYPT);
}
private function processed($login_password, $dbpassword){
$login_password = __STR.$login_password.__STR;
return password_verify($login_password, $dbpassword);
}
On each function call for creating a hashed string for password, the function returns the different string next time.
Ok, Let's go through this one by one.
First, it's hashing, not encryption. Encryption is two-way, hashing is one way. We want to hash. We never want to encrypt. Yes, terminology matters. Please use the correct terminology.
Next, each call to password_hash is supposed to return a different hash. That's because it's generating a strong random salt. This is how it was designed, and how you really should be using it.
Further, DO NOT do the "pepper" thing of adding __STR before and after the password. You're doing nothing but potentially weakening the users password (which is not good). If you want more information around why that's a bad idea: Read This Answer.
Continuing, I would highly recommend that you do not use crypt directly. It is actually surprisingly easy to screw up and generate extremely weak hashes. This is why the password_* api was designed. crypt is a low level library, you want to use a high level library in your code. For more information on ways to screw up bcrypt, check out my blog: Seven Ways To Screw Up Bcrypt.
The Password API was designed to be a simple, one-stop shop. If it's not working for you check the following things:
Are you using PHP >= 5.5.0? Or are you using PHP >= 5.3.7 with password_compat?
Is your database column wide enough?
It needs to be at least 60 characters long.
Are you checking that the result of the function is a string, and not bool(false)?
If there is an internal error, it will return a non-string from password_hash.
Are you getting any errors?
Have you turned on error_reporting to its maximum setting (I recommend -1 to catch everything) and checked that the code isn't throwing any errors?
Are you sure you are using it correctly?
function saveUser($username, $password) {
$hash = password_hash($password, PASSWORD_BCRYPT);
// save $username and $hash to db
}
function login($username, $password) {
// fetch $hash from db
return password_verify($password, $hash);
}
Note that each one should be called only once.
Are you using PHP < 5.3.7 with password_compat? If so, this is your problem. You are using the compatability library on an unsupported version of PHP. You may get it to work (certain RedHat distributions have backported the necessary fixes), but you are using an unsupported version. Please upgrade to a reasonable release.
If all else fails, please try running this code and reporting back the output:
$hash = '$2y$04$usesomesillystringfore7hnbRJHxXVLeakoG8K30oukPsA.ztMG';
$test = crypt("password", $hash);
$pass = $test == $hash;
echo "Test for functionality of compat library: " . ($pass ? "Pass" : "Fail");
echo "\n";
If that returns Fail, you are running an unsupported version of PHP and should upgrade. If it returns pass, than the error is somewhere in your logic (the library is functioning fine).
The best way to store passwords is to use PHP's function password_hash(). It automatically generates a cryptographically safe salt for each password and includes it in the resulting 60-character string. You won't have to worry about the salt at all!
// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($password, PASSWORD_BCRYPT);
// Check if the hash of the entered login password, matches the stored hash.
// The salt and the cost factor will be extracted from $existingHashFromDb.
$isPasswordCorrect = password_verify($password, $existingHashFromDb);
Your own scheme is very weak, first you are using MD5 which is ways too fast for generating password hashes, then you use a static salt, which defeats the purpose of a salt. Maybe you want to have a look at my tutorial about safely storing passwords.
Edit to answer updated question:
It is not necessary to add the __STR to the password (if you want to add a pepper there are better ways), but your example functions should actually work. The returned value of password_hash() will be different each time because of the random salt. This is correct, the function password_verify() is able to extract this salt for the verification. In your case the database field is probably the problem. Make sure it can hold a 60 character string.
I'm trying to find a hashing algorithm to use to save my passwords in the DB (along with a salt). I found this on SO: How do you use bcrypt for hashing passwords in PHP?. The top answer has a library that seems to work. I'm just a bit confused by the verify method.
To run the script, the author of the library provides:
$bcrypt = new Bcrypt(15);
$hash = $bcrypt->hash('password');
$isGood = $bcrypt->verify('password', $hash);
The hash method takes the input string and hashes it using mcrypt + a salt. The result is returned to $hash. Then the verify method takes the same input as hash method + $hash. verify then calls mcrypt + the value of $hash (instead of some random salt) -- and somehow the results are the same $hash???
Simplified:
hash = password + salt
verify = password + hash
verify = hash <-- huh???
What did I miss?
ALSO:
I plan to modify the code such that hash method accepts a $salt parameter. I will make getSalt a public method. This way, I can get a salt, store it in the DB, store the hash in the DB, and use user's input + salt to see if it hashes to what I have stored in the DB. Any problems with this idea?
I'm not familiar with the actual bcrypt, but something along the following lines must be what happens:
When you create the "hash", $bcrypt->hash('password') returns a string that contains both the hash value and the initial salt.
When you subsequently say $bcrypt->verify('password', $mystring), the function first reads the salt part out of your input string, then creates the hash again with that salt, and finally compares that computed hash with the hash part of your input string.
In any salinated hashing scheme, you will always have to store the salt along with the hash value.
The hash in this case also contains the salt. It's concatenated to the beginning of the string, usually as something like $2a$12$SALT.... Passing the hash to verify is just done to use that salt value again, the rest of the hash is ignored. That also means you do not need to modify the function for use with salts, that's already part of the algorithm.