I have created a registration form processed as follows:
function register_user() {
global $conn;
$name = sanitize($_POST['name']);
$email = sanitize($_POST['email']);
$password = sanitize($_POST['password']);
$salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
$saltedPW = $password . $salt;
$hashedPW = hash('sha256', $saltedPW);
mysqli_query($conn, "INSERT INTO users (name, email, password, salt) VALUES ('$name', '$email', '$hashedPW', '$salt')");
The login form is then processed as follows:
function login($email, $password) {
global $conn;
$user_id = user_id_from_username($email);
$query = mysqli_query($conn, "SELECT salt FROM users WHERE email = '$email'");
$row1 = mysqli_fetch_assoc($query);
$salt = $row1['salt'];
$saltedPW = $password . $salt;
$hashedPW = hash('sha256', $saltedPW);
$result = mysqli_query($conn, "SELECT COUNT(user_id) FROM users WHERE email = '$email' AND password = '$hashedPW'");
$row = mysqli_fetch_row($result);
return ($row[0] == 1) ? $user_id : false;
}
The user table is structured as follows:
user_id; name; email; password; salt (additional columns for password recovering, admin rights, etc.). Having submitted the details to my company's IT compliance department, someone with PHP experience (exceeding mine no doubt) has stated that I can't store the key for the encryption in the system - if someone got the file they could decrypt the password. My question is - is this correct? Is my process flawed? All of the research I have carried out indicates that an SHA256 hash with random salt is one of the best ways to go.
I can't store the key for the encryption in the system
You haven't. You're hashing, you're not encrypting. salt !== key
if someone got the file they could decrypt the password
No. Nothing is encrypted, so there's nothing to decrypt. They'd get only the resulting hash, which they'd still have to brute force.
Is my process flawed?
Yes, but not because of their comments. It's flawed because you should never use SHA or MD5 or similar for passwords. They're designed to be fast, which is not what you want. You want something that intentionally takes a hunk of CPU, as to make brute force attacks untimely. This is exactly what the password_hash() function is for. Use it.
Do not use sha256,md5 etc for password hashing. I think you should use BCrypt for password hashing. This is the most powerful password hashing algorithm at present. password_hash() and password_verify() are used to hash and verify password for BCrypt. PHP 5.5.0 or greater uses BCrypt as default algorithm for password_hash(). Do not use your own salt. Let password_hash() do it for you.
Related
Code Here is encrypting password but How I decrypt it or Compare it to login in laravel
Code where used
getsql(md5($_POST['regpassword'] . SALT), "default"),
md5 is hashing and it's not reversible you can't decrypt it you can only hash the password using the same algorithm and salt then compare the results to make sure that it's the correct password
When you're validating the password, you can do:
$hashed = md5($_POST['password'] . SALT);
$sql = "SELECT * FROM users WHERE username = '{$_POST['username']}' AND password = '$hashed'";
I've simplified this to show the important part of how to check the password, in reality you should use a prepared statement to prevent SQL injection.
Another way is to fetch the hashed password from the database, then compare it with the hashed+salted password that was given:
$hashed = md5($_POST['password'] . SALT);
$sql = "SELECT password FROM users WHERE username = '{$_POST['username']}'";
$result = mysqli_query($conn, $sql);
$row = mysqli_fetch_assoc($result);
if ($row && $hashed == $row['password']) {
// user is validated
}
If you fix your method of storing passwords to use a more reasonable method than a static SALT, this second method can easily be updated. Instead of $hashed == $row['password'] you would use password_verify($_POST['password'], $row['password']).
I am using the following two functions to hash passwords on a registration form:
function password_encrypt($password){
$hash_format = "$2y$10$"; //2y means use blowfish. 10 is cost parameter - number of times to run the blowfish hash.
//Blowfish wants salts of 22 characters or more.
$salt_length = 22;
$salt = generate_salt($salt_length);
$format_and_salt = $hash_format . $salt;
$hash = crypt($password, $format_and_salt); ///If this was echoed, salt would appear at beginning as part of result.
//Means hash can be passed in again as a salt. Then the has contains the salt making it easy to compare.
return $hash;
}
function generate_salt($length){
//MD5 returns 32 characters
$unique_random_string = md5(uniqid(mt_rand(), true));
//Valid characters for a salt are [a-zA-Z0-9./]
$base64_string = base64_encode($unique_random_string);
//But not '+' which is valid in base 64 encoding (base 64 encode returns '+' when it should be '.')
$modified_base64_string = str_replace('+','.', $base64_string);
//Truncate string to the correct length
$salt = substr($modified_base64_string, 0, $length);
return $salt;
}
When I run a check of the password with the following, data isn't being saved in the session:
if(!empty($User)&& !empty($Pass)){
$Pass = password_encrypt($Pass);
$query = "SELECT * FROM users WHERE '$User' = login AND '$Pass' = password";
$result = mysqli_query($connection, $query)
or die ('Error: '.mysql_error());
//If query produces nothing
if(!$query){
$Message = "Incorrect username and/or password.";
}else{
while($row = mysqli_fetch_assoc($result)){
///TODO Store user id in session as well
$_SESSION['first_name'] = $row["first_name"];
$_SESSION['group_id'] = $row["group_id"];
$_SESSION['user_id'] = $row["user_id"];
}
redirect_to('dashboard.php');
}
}
When I use plain text passwords instead of hashing the data is saved into the session correctly, however, when I use the hashing functions, accessing the session data I intended to save produces an error which says that, for example, the $_SESSION['user_id'] is an undefined index. Is there something wrong with the hashing methods?
The code is buggy and insecure and isn't going to work - the thread will never get to the point of trying to populate the session. But your question implies that you think it is getting there and there's something wrong with the session. There isn't.
The most elementary part of fault finding is isolating the problem - there are lots of ways of doing this, but instrumenting your code is a very simple way to do so. If you had you would know that your problem has nothing to do with the session.
You've not understood how salted hashes and initialization vectors work - you need to retrieved the hash from the password based on the suplied username then run it through crypt again in PHP land. There are lots of examples on the internet on how to do this.
Your problem is that you are not escaping characters in your encrypt password function:
$2aVOhEz8P7i6 will look for the variable '2aVOhEz8P7i6' which isn't set.
EDIT:
echo password_encrypt('hello');
= $2aVOhEz8P7i6
EDIT2:
also:
"SELECT * FROM users WHERE '$User' = login AND '$Pass' = password"
should be
"SELECT * FROM users WHERE login = '$User' AND password = '$Pass'"
for readability.
I want my login password to be secured. So I came up to use the PHP's crypt() function to hash the password before inserting it to database. But Im having trouble when comparing the user input password from the converted hash password. Here's my code:
<?php
$password = 'hello_password';
# A higher "cost" is more secure
$cost = 10;
# Create a random salt
$salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');
# Blowfish algorithm.
$salt = sprintf("$2a$%02d$", $cost) . $salt;
$salted_password = $password . $salt; // apply salt to password
# hash the password
$hash_password = hash('sha256', $salted_password);
$userInput = 'hello_password'; // suppose this is the user input password
if (hash('sha256',$userInput) == $password) {
echo "Password Verified.";
}
else {
echo "Incorrect Password";
}
?>
But it always displays Incorrect Password although my password is correct. I don't want to use "hash_equals" function as it is not supported with my current PHP version. Can someone help me with this ? Thanks
You're comparing a hashed user input to the actual user password. So of course this is never going to work.
You're basically asking if hash == 'hello_password'. A hash will never match that, that is the whole point of a hash. You also aren't using the salt with the user input.
You hash the actual password with a salt which is fine:
$salted_password = $password . $salt; // apply salt to password
# hash the password
$hash_password = hash('sha256', $salted_password);
So you need to hash the user input with the salt, the same way:
$salted_input = $userInput . $salt; // apply salt to user input
# hash the input
$hash_input = hash('sha256', $salted_input);
Then you can compare $hash_input with $hash_password.
You also aren't using a salt properly. The salt is supposed to be used in the storage of the password to prevent rainbow table attacks. Randomly generating a salt to apply to both the input and the password at the time of comparison is pointless.
I'm trying to hash the password if you sign up on my website, but it doesn't work.
This is my code to hash the password upon signing up:
$escapedName = mysql_real_escape_string($_POST['user']);
$escapedPW = mysql_real_escape_string($_POST['password']);
$salt = bin2hex(mcrypt_create_iv(32, MCRYPT_DEV_URANDOM));
$hashedPW = hash('sha256', $escapedPW . $salt);
Then I just insert it in a database (the hashed password and the salt).
For example, when I hash Sas, with the following salt:
abac7ad23185ad19967f0d13e962197962782f0b7ec32d9889c27a93a9e800fa
This is the hashed password:
8ca5c5f31fafbf382533dbcbfc22b3635d776ec7770c7eac58d8ef9f1fa3613c
But when I try to hash the password on log in, with the exact same password and salt, this becomes the hashed pass:
6eb4b16444f18cee19db32bd29a39970e3019c5b1972a982ae4cb9a59642dffc
This is the code I use to login:
$escapedName = mysql_real_escape_string($_POST['user']);
$escapedPW = mysql_real_escape_string($_POST['password']);
$saltQuery = mysql_query("SELECT salt FROM members WHERE user='{$escapedName}'");
while($result = mysql_fetch_assoc($saltQuery)) {
$salt = $result['salt'];
}
$hashedPW = hash('sha256', $escapedPW . $salt);
$sql = mysql_query("SELECT * FROM members WHERE user='$escapedName' AND pass='$hashedPW'; ");
while ($res = mysql_fetch_assoc($query2)) {
$username = $res['user'];
$PW = $res['pass'];
}
I hope it's not too much code and I also hope you will understand my question.
Sorry I can't comment but something tells me that there is a length restriction on the salt column in your database.
Example: The salt field might only allow a 64 characters while the generated salt might be longer therefore when you save the salt it gets trimmed which ultimately changes the hashed password.
If that's the case, you might want to trim the salt before saving it.
I'd advise using PDO to make queries against your database.
Your inputs to the hash function must be different for some reason or other.
Add log output messages that print your inputs before you hash for both use cases (create user and login). Also be sure to put quotes around the inputs in your logging to show whitespace issues.
Compare the raw inputs as well as the output of the hash function in both cases, and there will be a difference somewhere. If there is no difference, and the output of the hash is the same, then there is a problem in your query that is looking up the user in the login case.
Whatever you're doing, it's insecure if you WANT the hashes to be the same! See http://php.net/crypt for proper password hashing.
All you need:
function check_password($password) {
...//get db password to compare
if (crypt($post_password, $db_results[0]['password']) == $db_results[0]['password']) {
return true;
} else { return false; }
}
I have been trying to learn about hashes and salts stored in a users table withing a mysql DB. I get through with storing them but can't seem to wrap my head around how to validate when the user logs in.
I have looked through and seen about storing the salt and hash seperately and together. The salt that I am producing is random.
Any ideas?
I have posted my code.
<?php
$password = 'passwordwhatever';
//generate the salt
function gen_salt() {
$salt = uniqid(mt_rand(), true) . sha1(uniqid(mt_rand(), true));
$salt = crypt('sha512', $salt);
return $salt;
}
//generate the hash
function gen_hash($salt, $password) {
$hash = $salt . $password;
for($i = 0; $i < 100000; $i++) {
$hash = crypt('sha512', $hash);
}
$hash = $salt . $hash;
return $hash;
}
$password = gen_hash(gen_salt(), $password);
echo $password;
?>
As long as you produce the same salt, it shouldn't matter too much. Store it in your db, in configuration, as long as you can get to it. The effort of reversing a SHA512 hash is so high that unless you're the Pentagon, nobody will bother. The point is that you want to be able to repeat the hash with the same salt so you can be very sure that the input was the same without having to store the sensitive input. If that makes sense.
The salt is going to need to be contained somewhere within the database (or somewhere). Some options are to append the salt to the hash or stored as its own field. I like appending it to the hash.
$password = $salt . '.' . $hash;
Then when the user goes to login, you grab his password, break it into the hash and the salt, and use your password generation function (with the salt from the password instead of a random salt) to determine if it matches the password in the db.
list($salt,$hash) = explode('.', $password);
$check = gen_hash($salt, $input_pass);
if ($check === $password)
// valid
Maybe you should take a look on bcrypt. This might help you. I wrote some tutorials but they are in german. Oh and PHP 5.3 support bcrypt natively.