I am making a login system in PHP and I was wondering if this current hash function I have is secure enough.
public function genHash( $user, $pass )
{
$user = strtoupper($user);
$staticSalt = $this->staticSalt;
$dynamicSalt = hash('SHA512', md5($user . $pass) . sha1($pass) . hash('SHA512', $user . $pass));
$final = hash('WHIRLPOOL', $pass . $dynamicSalt . $staticSalt);
return $final;
}
The static salt is just a bunch of random characters. Anyway, how can I make it more secure?
You could use different salts for each user and store them in the database but besides that this system looks pretty secure. (Not knowing the details of the server).
EDIT:
Theoretically multihashing a string increases the chance of hash collisions but I haven't found anything reliable that says this is a practical risk.
Related
I'm obviously new to blowfish encryption to be asking this. I believe to have one side of the equation figured out but cannot figure out how to login once the hash is in the DB. I have the following for encrypting the password on registration:
$blowfish_hash = "$2y$10$";
$salt_length = 22;
$salt = Generate_Salt($salt_length);
$hash_combined = $blowfish_hash . $salt;
$hash = crypt($password, $hash_combined);
$password = $hash;
The Generate_Salt() function is as follows:
function Generate_Salt($length) {
$unique_rndm_str = md5(uniqid(mt_rand(), true));
$base64_string = base64_encode($unique_rndm_str);
$mod_Base64_str = str_replace('+', '.', $base64_string);
$salt = substr($mod_Base64_str, 0, $length);
return $salt;
}
Once I register I get this nice long hash - great! but, when I go to login I'm unsure on how to call the hash to check against the given password: $_POST['log_password'];
Using md5 is was easy, I just encrypted this way $password = md5($password); and recalled this way $password = md5($_POST['log_password']); however reading up I realize that this is not a secure method.
I've been at this for hours, can anyone shed some light on this for me please? Any help would be appreciated.
ep
It is much easier than you think. Just use the function password_hash() instead, it will do the call to the crypt() function and handles the generation of a safe salt.
// Hash a new password for storing in the database.
// The function automatically generates a cryptographically safe salt.
$hashToStoreInDb = password_hash($_POST['password'], PASSWORD_DEFAULT);
// 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($_POST['password'], $existingHashFromDb);
I have a processing file for a login to an app. I either do not understand the purpose of password_needs_rehash() or it is not working. The login is authenticating and passing me through to the correct page. But I can't get the code to even echo the new hash.
Am I doing this correctly?
Does the if not throw a new hash because it does not need rehashed? If so when would a password need rehashed if it was properly hashed and stored in the DB?
My processing file is below:
$hash = $row['hash'];
$userPassword = $_POST["li_password"];
if (password_verify($userPassword, $hash)) {
if ( password_needs_rehash($hash, PASSWORD_DEFAULT, ['cost' => 12]) ) {
$newhash = password_hash($userPassword, PASSWORD_DEFAULT, ['cost' => 12]);
echo $newhash;
}
} else {
header('Location: http://' . $_SERVER['HTTP_HOST'] . '?error=loginfailed');
exit();
}
The function password_needs_rehash() only needs to be used if you change the $options which usually refers to the cost.
The more the cost, the more CPU time it takes to hash the password but the more difficult it becomes to crack it. If you change hosting or move over to a cloud based system where multiple computers can calculate the hash for you, you are able to increment it at your own discretion.
You only need to check if the password needs a rehash at user login, since password_verify() can still verify the password if you changed the $options. If password_needs_rehash() returns true at that point, use password_hash() with the new options and replace the old hash.
if (password_verify($_POST["li_password"], $row['hash'])) {
// valid login
if (password_needs_rehash($row['hash'], PASSWORD_DEFAULT, $options = ['cost' => 12])) {
$newhash = password_hash($_POST["li_password"], PASSWORD_DEFAULT, $options);
// store new hash in db.
}
} else {
header('Location: http://' . $_SERVER['HTTP_HOST'] . '?error=loginfailed');
exit();
}
I have found this post helpful MySQL password() function to PHP but I am having trouble applying the solution offered there to my problem.
A password was stored in a Mysql using Password(). I want to adapt this script to compare the entered password with the one stored in the database, rather than use the 'crypt()' function.
public function authenticate($user,$pass) {
$mysqli = new mysqli(DBHOST,DBUSER,DBPASS,DB);
if ($mysqli->connect_errno) {
error_log("Cannot connect to MySQL: " . $mysqli->connect_error);
return false;
}
$safeUser = $mysqli->real_escape_string($user);
$incomingPassword = $mysqli->real_escape_string($pass);
$query = "SELECT * from users WHERE username ='{$safeUser}'";
if (!$result = $mysqli->query($query)) {
error_log("Cannot retrieve account for {$user}");
return false;
}
// Will be only one row, so no while() loop needed
$row = $result->fetch_assoc();
$dbPassword = $row['password'];
if (crypt($incomingPassword,$dbPassword) != $dbPassword) {
error_log("Passwords for {$user} don't match");
return false;
}
$this->id = $row['id'];
$this->firstName = $row['first_name'];
$this->lastName = $row['last_name'];
$this->username = $row['username'];
$this->email = $row['email'];
$this->dateJoin = $row['dateJoin'];
$this->school = $row['school'];
$this->level = $row['level'];
$this->isLoggedIn = true;
$this->_setSession();
return true;
} //end function authenticate
Is there an easy way to adapt this script? Do I just add
AND `password` = PASSWORD('{$incomingPassword}')
to my query? This seems a little clumsy.
Are you really sure the passwords where hashed with the MySql Password() function, because this function is not meant to be used in applications? It is not possible to store passwords safely and verify passwords in an SQL-query directly.
You really should use a slow hashing function like BCrypt, and salting is mandatory. That means, that you need a two step process, first get the stored password hash by username with an SQL-query, then extract the salt from the hash and do the verification.
The recommended way to hash passwords with PHP is the new function password_hash():
// 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);
If you are interested in more in-depth information about this topic, you can have a look at my tutorial about safely storing passwords.
Is it a good way to change password value each time a user log in to the database?
I have wrote a hash function to hash the password when a user register a new account on the system.
Each time the user logs in, the hash value in the database will be changed. Is it good or bad?
If you designed this hash function all by your self then... It is a very very bad idea. Why would you need something like this? If you store salted SHA-256 hashed passwords the security is good enough. You do not need to regenerate passwords, it does not provide any additional security. If lets say your app is prone to SQL-Injection, then this scheme won't protect your app. You would be a lot better if you used salted and keyed SHA-256, something like this: (I'm not a php coder, I just want our apps to be secure)
$username = 'Admin';
$password = 'gf45_gdf#4hg';
$key = 'MySuperSecretKEY!!!!';
$salt = hash('sha256', uniqid(mt_rand(), true) . 'something random' . strtolower($username));
$hash = $salt . $password . $key;
$hash = hash('sha256', $hash);
$hash = $salt . $hash;
and then checking:
$username = 'Admin';
$password = 'gf45_gdf#4hg';
$sql = '
SELECT
`hash`
FROM `users`
WHERE
`username` = "' . mysql_real_escape_string($username) . '"
LIMIT 1
;';
$r = mysql_fetch_assoc(mysql_query($sql));
$salt = substr($r['hash'], 0, 64);
$hash = $salt . $password . $key;
$hash = hash('sha256', $hash);
$hash = $salt . $hash;
if ( $hash == $r['hash'] ) {
//OK
}
So even if attacker will be able to trick the salting algorithm he does not know, a key so he won't be able to reproduce a valid hash in SQL-Injection attack.
I have registered a new user and saved the username, password & salt in the DB using the following hashing method:
if(isset($_POST['register']))
{
$password = $_POST['password']
function sanitize($data)
{
$data=trim($data);
$data=htmlspecialchars($data);
$data=mysql_real_escape_string($data);
return $data;
}
$password = sanitize($password);
function createSalt()
{
$salt = bin2hex(mcrypt_create_iv(32,MYCRYPT_DEV_URANDOM));
$hash = hash("sha256", $salt);
$final = $salt.$hash;
return $final;
}
$hashedPassword = hash("sha256", $password);
$salt = createSalt();
$hashedPassword = hash("sha256", $hashedPassword.$salt);
$query = sprintf("INSERT INTO users(username, password, salt) VALUES('%s','%s','%s')",$username, $hashedPassword, $salt);
}
And Later while trying the login.php, I am entering the same password which I saved during registration and using the below code to check if the entered password is the same as the one in the DB
if(isset($_POST['login']]))
{
$password = $_POST['password']
function sanitize($data)
{
$data=trim($data);
$data=htmlspecialchars($data);
$data=mysql_real_escape_string($data);
return $data;
}
function validateUser()
{
session_regenerate_id (); //this is a security measure
$_SESSION['valid'] = 1;
$_SESSION['username'] = $username;
}
$password = sanitize($password);
$query = sprintf("SELECT * FROM users WHERE username = '%s'",$username);
$sql = mysql_query($query);
$count = mysql_num_rows($sql);
$row = mysql_fetch_array($sql);
if($count<1)
{
echo $count;
unset($_POST['login']);
header("location:login.php");
exit;
}
$hash = hash("sha256", $password);
$salt = $row['salt'];
$hash = hash("sha256",$hash.$salt);
echo $hash."<br />".$row['password']."<br /><br />";
if($hash != $row['password'])
{
unset($_POST['login']);
header("location:login.php");
exit;
}
else
{
validateUser();
unset($_POST['login']);
header("location:index.php");
exit;
}
}
These passwords are not getting matched.
Kindly let me know what's wrong in this code.
There is nothing wrong with your code.
the salt value stored in the database is truncated because the varchar value is low increase the varchar value of your salt column to 200-300 something and than try this.. it will run fine.
I facepalmed when I found out this was screwing the result..
Dins
Actually i didn't see why this should not work, the code you have shown, should produce the same value, maybe you could check, whether the salt you read from the database is really the same as you wrote to the database.
Nevertheless i would not engourage to go further on this route, there are quite a lot of problems here.
First of all, SHA-256 is not a good choice to hash passwords, instead use a slow key-derivation function like BCrypt.
You should not escape input data without need, and if you need to escape them, you should do it only for the specific target system (htmlspecialchars and mysql_real_escape_string make no sense if you are going to calculate a hash anyway).
To create a salt, you use the random source, that is good. Using a hash afterwards creating the salt, will in no way make the salt more random.
There is no need to have two separate fields for password and salt in the database. Php's crypt() function will create a hash value, that already contains the salt.
I would invite you to read this tutorial about hashing passwords, you will find a PHP example too, and i would recommend to use the phpass library.