(PHP) How to correctly implement crypt() - php

Here is the example from the PHP manual page for crypt():
<?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!";
}
?>
Why does this work? I take it 'mypassword' is the password I want the actual admin to use. So I crypt that first, and set it equal to $password. Obviously, I must need to store that in the DB. But in the next lines it's being used as both the salt and what I'm comparing to, and I don't understand how crypt($user_input, $password) can possibly be equal to $password, if in this latter case I have ideally the right password as $user_input but salted with $password being compared to $password. It would make more sense to me if the last line were
if (crypt($user_input) == $password) {
echo "Password verified!";
}
What am I not understanding?

crypt is a one-way function and returns a string that already contains the salt. The output is similar to what is stored in /etc/shadow.
Example from php.net:
<?php
echo 'result: ' . crypt('somepassword');
echo 'result: ' . crypt('somepassword');
echo 'result: ' . crypt('somepassword');
?>
result: $1$K2D8DGwq$b05uO37aMwO4rnDlB9Rsi1
result: $1$aPBvu2y.$213YVEs8/5m.jMCXSScly/
result: $1$dW3Xu2p6$nuCtJe2zzlgBMLxN2oZCx/
When comparing the user input with the crypt result, the function automatically extracts the salt from the string.

Related

sha1() for Password hashing

I am using sha1 for my password security. I have stored password in this way in register.php
// secure password
$salt = openssl_random_pseudo_bytes(20);
$secured_password = sha1($password . $salt);
//Send it to mysql table
$result = $access->registerUser($username, $secured_password, $salt, $email, $fullname);
This all is working fine.
Problem is here:
In my login.php
$password = htmlentities($_POST["password"]);
$secure_password = $user["password"];
$salt = $user["salt"];
// 4.2 Check if entered passwords match with password from database
if ($secure_password == sha1($password . $salt)) {
//do something
} else {
//do something
}
I am always getting as password does not match.
where am I going wrong?
First is first. NEVER USE SHA OR MCRYPT TO STORE YOUR PASSWORD.
EDIT : The password_hash() function generates a long password hash, so make sure that your column in the mysql is a VARCHAR of 500 space
All these useless practises is the root reason why almost many websites get hacked. To tackle the situation, php did a lot of research and then at last came with the most secure function called the password_hash(). I am not more onto explaining about password_hash() here as there are already many documents on the internet.
You can always hash a password like this
<?php
$securePassword = password_hash($_POST['password'], PASSWORD_DEFAULT);
$query = $db->query('INSERT INTO users ......');
?>
And, to verify the password, you can simply use this function
<?php
$passwordHash = $query['password']; //Password from database
$userPassword = $_POST['password']; //Password from form
if(password_verify($userPassword, $passwordHash)) {
echo 'Password is correct, logged in!';
} else {
echo 'Password is wrong, try again';
}
?>
And, answer for your question.
PLEASE DON'T USE SHA OR MCRYPT OR BCRYPT. IF YOU WANNA GET YOUR WEBSITE HACKED, THEN CONTINUE. OR USE password_hash()
The reason you don't get the hash genereated each time because the openssl_random_pseudo_bytes() generates random numbers each time. So each time, during execution, the function returns different numbers and you get your sha result wrong and thus giving a FALSE alert.
PLEASE, AGAIN. I BEG YOU TO USE password_hash() FUNCTION
For more information on password_hash() and password_verify() :
http://php.net/manual/en/function.password-hash.php
http://php.net/manual/en/function.password-verify.php

crypt() returning *0 when passing in salt as variable

I am currently trying to code a login/registration system for a school project.
The password upon registration is encrypted using crypt() and a randomly generated salt and stored in the database.
When logging in, the salt and hash are retrieved from the database together and split using the explode function (I have stored them in salt.hash format, so using . to delimit).
I am then trying to use the user input password along with the salt retrieved from the database to generate a hash and compare it to the original taken from the database. This is where my problem lies.
function fetch_hash($username, $dbc)
{
$fetch_hash = $dbc->prepare("SELECT * FROM users WHERE username = :username");
$fetch_hash->bindValue(":username", $username);
$fetch_hash->execute();
$result = $fetch_hash->fetchAll();
$hash = $result[0][password];
return $hash;
}
$correct_hash = fetch_hash($username, $dbc);
list($salt, $hash) = explode('.', $correct_hash);
$password = "password";
echo "Generated hash : " . crypt($password, $salt); // Returns *0
echo "Generated hash 2: " . crypt($password, "$2y$12$p7MTIQRBzetIWkH5zeqr5"); // Returns the correct hash which matches the on stored in the database
When passing in $salt as a parameter for crypt(), it returns *0 despite having the value of "$2y$12$p7MTIQRBzetIWkH5zeqr5" and when I enter it directly, it will return the correct hash to match the one stored in the database (i.e the login is correct). I really don't understand why it would not work as they are the exact same value.
Thanks in advance for any help :)
With crypt() you don't have to store hash and salt separately or delimit it in any way. crypt is totally able to work with its output:
// in register/password update/etc
$hash = crypt($password, $randomSalt);
// store to database
// in login routine
// retrieve hash as it was stored, no additional processing
if ($hash == crypt($password, $hash)) {
// password matches, proceed with user authentication, sessions etc.
} else {
// password mismatch
}
However, there's a relatively new API specifically intended to work with passwords: password_* functions. It's more intuitive to use and has additional security measures crypt() does not (like resistance to time-based attacks). If your php installation is old (older than 5.5), you can use this PHP polyfill and still use the same API.
Try this.
$correct_hash = '$2y$12$p7MTIQRBzetIWkH5zeqr5.$2mV0NZp92R3g';
list($salt, $hash) = explode('.', $correct_hash);
$password = 'password';
echo "Generated hash : " . crypt($password, $salt . '$'); // Returns *0
echo "Generated hash 2: " . crypt($password, '$2y$12$p7MTIQRBzetIWkH5zeqr5' . '$');

How to verify an hashed password

I am using the password_hash() function.
Now it works to hash the password, but how do I verify it?
Well the function for this option is called: password_verify.
How it does work is this;
<?php
$password = "[PASS]"; //Password user fill in.
$hash= "[HASH]"; //The hashed password that you saved.
$checkPass = password_verify($password, $hash); //This returns a boolean; true or false
if ($checkPass == true)
{
echo 'Password is good!';
}
else
{
echo 'Password is wrong!';
}
?>
boolean password_verify ( string $password , string $hash )
Verifies that the given hash matches the given password.
Note that 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 verify function to verify the hash without needing separate storage for the salt or algorithm information.
password
The user's password.
hash
A hash created by password_hash()
http://php.net/manual/en/function.password-verify.php

PHP encrypt password before storing it to database

I found the following example on php.net to secure password before storing it. However, I don't quite understand line 3. Could someone explain this a little bit? Thanks!
1 <?php
2 $password = crypt('mypassword'); // let the salt be automatically generated
3 if (crypt($user_input, $password) == $password) {
4 echo "Password verified!";
5 }
6 ?>
crypt is a one-way function and returns a string that already contains the salt,
When comparing the user input with the crypt result, the function automatically extracts the salt from the string.
To be more clear :
crypt() outputs a string that contains both the salt and the result of the hash. When you pass it that string as a salt, it knows to extract only the salt part and ignore the hash part. And it still returns a string containing both the salt and the hash. So these strings can be compared
You can clearly understand by:
when user signups for the first time , the process is:
$password = crypt($user_input); // let the salt be automatically generated
if (crypt($user_input, $password) == $password) {
echo "Password verified!";
}
when user tries to login , the process will be :
if(crypt($user_passsword_currentlyin_db, $user_inputted_password) == $user_inputted_password) {
echo "Password verified!";
}
Hope you get the thing :)
EDIT:
The output of crypt consists of:
When you pass this output as "salt" back to crypt, it will extract the right algorithm and salt, and use these for the operation. If there is only an algorithm mentioned, it uses this one and generate random salt. Otherwise it will choose a default algorithm and generate random salt. The hash part in the passed salt parameter is ignored.
So you can simply compare your stored_hash with crypt(password, stored_hash) - if it is equal, it quite likely was the right password.
Here is an pseudocode explanation (in PHP-like syntax) how crypt works:
function crypt($password, $salt)
{
if (substr($salt,0 1) == "_") {
$count = substr($salt, 1, 4);
$real_salt = substr($salt, 5, 4);
return "_" . $count . $real_salt . crypt_ext_des($password, $count, $salt);
}
if(substr($salt, 0, 3) == "$1$") {
list($ignored, $real_salt, $ignored) = explode("$", $salt);
return "$1$" . $real_salt . "$" . crypt_md5($password, $real_salt);
}
if(substr($salt, 0, 4) == "$2a$") {
$cost = substr($salt, 4, 2);
$real_salt = substr($salt, 7, 22);
return "$2a$" . $cost . "$" . $real_salt . crypt_brypt($password, $real_salt, $cost);
}
// ... SHA256 and SHA512 analogons
// no match => STD_DES
$real_salt = substr($salt, 0, 2);
return $real_salt . crypt_std_des($password, $real_salt);
}
It is considered good security practice not to store plain text passwords on the server side in case the password file is compromised and user's passwords revealed.
Instead, when the user registers, you should use a one-way hash algorithm on the password before storing it in the database. This way, it is impossible to reproduce the user's password if the file is compromised.
When the user logs in, it is not possible to compare the plaintext password they just entered with that in the database because the password in the database has been hashed.
However, because the same input to a hash function will always produce the same output, line #3 will hash the plaintext input the user just entered and compare it with the hash stored in the database. That way you are comparing hashes with hashes and can establish if the user is authenticated.
A couple notes on security: you should always fail-safe, so your code should read:
if(crypt($user_input, $password) == $password) {
echo "Password verified!";
} else { die("Wrong username/password"); }
Also note that a compromised password file can be reconstructed using a rainbow table.
$user_input is nothing but your password which is stored in the DATABASE
if(crypt($stored_passsword_in_db, $current_password) == $current_password) {
echo "Password verified!";
}
Hope this helps you....
The second parameter is the key. That accepts either nothing, a salt, or a hash that crypt made earlier, which has the salt to make the hash, embedded in it. In the first case, a salt is made up. In the latter case, the salt is used and not the entire hash. So therefore this holds (make sure you understand that, because that's the key to understanding line 3):
crypt($input, $salt) == crypt($input, crypt($input, $salt))
What you have in your database as the user's hashed password, is the left term in that equation. So line 3 can be rewritten as:
if (crypt($user_input, crypt('mypassword')) == crypt('mypassword'))
And the salt was automatically generated in line 2, let's assume it equals 'autosalt'. Line 3 can now be rewritten to this:
if (crypt($user_input, crypt('mypassword', 'autosalt')) == crypt('mypassword', 'autosalt')
And there you go: this is only true if $user_input == 'mypassword'.

Issue with extra character in MD5 Hash?

I've got a login script that I'm just starting on. When a new password is entered it is first encrypted using MD5, then stored in the database.
When I type the username and password in my login form and submit it, I'm trying to verify the stored password against a $_POST variable like this:
$username = $_POST['username'];
$password = md5($_POST['password']);
//database stuff here
$q = mysql_query("SELECT * FROM Users WHERE username='$username'");
while ($row = mysql_fetch_array($q))
{
if ($row['password'] == $password)
{
echo "Passwords match.";
}
else
{
echo "Password is incorrect.";
echo "<br />Entered password: " . $password;
echo "<br />Stored password: " . $row['password'];
}
}
This is just in the testing stages, so the password that I'm attempting to verify is 'password', for simplicity. If I output $_POST['password'], I get password - however, if I output the MD5 hash as stored in the database and md5($_POST['password']), they do not match. The latter has extra characters. Anyone know why this is happening?
Your column type could be truncating the data when you store it.
Check that it is at least 32 characters (the size of an MD5 hash).
Also, this script is vulnerable to SQL injection.
Your field is too short. MD5 hashes have 32 hexadecimal digits.
It is possible to get tricked. Check this:
$str=md5('password');
print(strlen($str));
and the output is 32. (the string is "5f4dcc3b5aa765d61d8327deb882cf99")
Now try inserting this within html code:
<?=print(md5('password'));?>
And the output is "5f4dcc3b5aa765d61d8327deb882cf991" (33 chars)
The issue gets clearer when you insert:
<?=print(strlen(md5('password')));?>
The output is "321"
This works:
<?php print(strlen(md5('password')));?>
Despite the other answers, MD5 as an algorithm does not produce hexadecimal characters at all. MD5 is an operation that is performed on binary data. As output, it returns 16 bytes of binary data.
It's the PHP function that returns a hexadecimal string. It depends on the way you want to handle the output of the hash if this is what you want. If you store the hash as binary data you might want to use the "raw" output:
string md5 ( string $str [, bool $raw_output = false ] )

Categories