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.
Related
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 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.
i'm testing several combinations of sha1 and md5:
<?php
$test = 'fail test';
echo nl2br ("Text: $test\n");
echo nl2br ("md5: ".md5($test)."\nsha1: ".sha1($test)."\nsha1(md5): ".sha1(md5($test))."\nmd5(sha1): ".md5(sha1($test)));
?>
Output:
Text: fail test
md5: 748410d0085967c496d54dd8fcbecc96
sha1: d730125e8cb8576459173655148fb6896ef44c09
sha1(md5): faa3ebeecfec45e509e93e6b245a69e2a78785ea
md5(sha1): b48e89b85c350c91eb302c1de96d4249
Which one better, or maybe user something else ? If yes, what then ?
Both of them are cryptographic hash functions that operate 1-way only, the main difference being that MD5 output size is 128 bits whereas SHA-1 is 160 bits. In brief, I don't see they are much different to use despite MD5 is more common these days.
Curiously, I can't really see how md5($text) is different from md5(sha($text)) when they all encrypted to a 32 character-long string, what about md5($text."token") for example?
And, what do you mean by better? Is it more good looking or more security? See bcrypt if you prefer security :) Wikipedia: http://en.wikipedia.org/wiki/Bcrypt
Hashing a hash adds no extra security. (In fact, it might make it worse if the person has a hash-of-hash lookup table.)
The best hash will be the one that is computationally the most expensive to perform without any vulnerabilities. I would hash passwords with at least sha-256.
Always hash your passwords with a salted key. This key should be unique per password. It doesn't need to be stored privately. The purpose of a salted password is that the hacker who gained access to your database cannot simply compare the hash with a known list of hashes that correspond to common passwords. Instead, he must try to brute force the password by trying every possible password.
By using a unique salt per password, you guarantee that each hash in the database is different, even if they use the same password.
To salt a password, simply create a random string of characters and append it to the password. Here's a sample hash with a 48-bit salt and sha-256:
function make_password($password)
{
# random 48-bit salt (8 chars when base64 encoded)
$salt = base64_encode(pack('S3', mt_rand(0,0xffff), mt_rand(0,0xffff), mt_rand(0, 0xffff)));
return $salt.hash('sha256', $salt.$password);
}
function check_password($password, $hash)
{
$salt = substr($hash, 0, 8);
return hash('sha256', $salt.$password) == substr($hash, 8);
}
$password = 'password';
$hash = make_password('password');
echo $hash."\n";
var_dump(check_password('password', $hash));
var_dump(check_password('wrong', $hash));
Every time you run it, the hash will be different. To validate a password, you select the row where the username matches, and then call check_password($password_from_user, $hash_from_db).
Here's a sample output:
AzrD1jZzc693714a43ad5dfd4106c0a620ef23ff9915070711fa170a6670b8164862b496
bool(true)
bool(false)
You can use a larger salt or a stronger hashing algorithm if you prefer. But at minimum, I would use something like the above.
You should salt your passwords, ALWAYS. This doesn't stop brute force through a login form but if someone managed to get the details, it would be much harder to crack (rainbow tables would be useless unless they manage to get your salt too)
Essentially, if you adding onto the original data or mangling in a controlled way, it will make security a little better. No-one can ever reverse a hash but they can find other inputs thats match the hash. Mangling the user input will make it harder to login for the hackers.
for example, if a user's pass is 123456, if you add a salt of "salt" to it so it becomes 123456salt, the MD5 of this would be 207acd61a3c1bd506d7e9a4535359f8a. A hacker could crack this to become 123456salt but when it comes to using that on your login form, your code will add salt again and the login will fail.
1) How do you create secure Blowfish hashes of passwords with crypt()?
$hash = crypt('somePassword', '$2a$07$nGYCCmhrzjrgdcxjH$');
1a) What is the significance of "$2a"? Does it just indicate that the Blowfish algorithm should be used?
1b) What is the significance of "$07"? Does a higher value imply a more secure hash?
1c) What is the significance of "$nGYCCmhrzjrgdcxjH$"? Is this the salt that will be used? Should this be randomly generated? Hard-coded?
2) How do you store Blowfish hashes?
echo $hash;
//Output: $2a$07$nGYCCmhrzjrgdcxjH$$$$.xLJMTJxaRa12DnhpAJmKQw.NXXZHgyq
2a) What part of this should be stored in the database?
2b) What data type should be used for the column (MySQL)?
3) How should one verify a login attempt?
You should store the entire output of crypt, there's not a lot of point in splitting it up, because you need to generate a new salt for each password you're hashing in any case. Using a fixed hidden salt as mentioned by Matt is wrong - the salt should be different for every hash.
For more information see http://www.openwall.com/articles/PHP-Users-Passwords - I recommend using the phpass library because it handles generating a random salt for you, unlike crypt().
1a) Strength of encryption - requirement in the range of 4..31. See http://php.net/manual/en/function.crypt.php
1b) See 1a
1c) See 1a. 'salt' should not be random, or you would not be able to regenerate the same hash for a given input - see 3.
2a) Strictly speaking, everything except the hash (in case database is compromised). Also, store your salt in a file not accessible beneath the web server's document root and include it. Set it with the strictest permissions possible; ideally read only to web host service (e.g. apache), no write or execute privileges. Less strictly speaking, depends how defensive you wish to be against hackers. Not storing the salt just makes life more difficult; they still have to get the data being input to the algorithm right - but why make it easier?
2b) VARCHAR(32) should be fine for blowfish, if not storing the hash
3) Assuming you've already run the proper injection prevention code, etc.. so please don't just copy the below blindly (and ideally use PDO instead of mysql extension). The below is specific to blowfish, SHA-256 and SHA-512 which all return the salt within the hash. Would need modification for other algorithms...
//store this in another file outside web directory and include it
$salt = '$2a$07$somevalidbutrandomchars$'
...
//combine username + password to give algorithm more chars to work with
$password_hash = crypt($valid_username . $valid_password, $salt)
//Anything less than 13 chars is a failure (see manual)
if (strlen($password_hash) < 13 || $password_hash == $salt)
then die('Invalid blowfish result');
//Drop the salt from beginning of the hash result.
//Note, irrespective of number of chars provided, algorithm will always
//use the number defined in constant CRYPT_SALT_LENGTH
$trimmed_password_hash = substring($password_hash, CRYPT_SALT_LENGTH);
mysql_query("INSERT INTO `users` (username,p assword_hash) VALUES '$valid_username', '$trimmed_password_hash'");
...
$dbRes = mysql_query("SELECT password_hash FROM `users` WHERE username = '$user_input_username' LIMIT 1");
//re-apply salt to output of database and re-run algorithm testing for match
if (substring($salt, CRYPT_SALT_LENGTH) . mysql_result($dbRes, 0, 'password_hash') ) ===
crypt($user_input_username . $user_input_password, $salt) ) {
//... do stuff for validated user
}