So I'm using the new PHP 5.5 Password Hashing API, and I'm not sure if I got this correctly.
I've tried automatically rehashing every login and sometimes I fail, even when the hashing turns out to be the same anyways, I feel like I'm doing something wrong.
It could be the query function that I probably got wrong, because the hashes don't even change when I check phpMyAdmin.
if (password_needs_rehash($result_row->user_password_hash, PASSWORD_DEFAULT))
{
$newhash = password_hash(
$_POST['user_password'], PASSWORD_BCRYPT,
['cost' => 12, 'salt' => 'superfreakingsonicdude',]
);
// update hash in database
$this->connection->query(
"UPDATE users SET user_password_hash='" . $newhash .
"' WHERE user_name='".$result_row->user_name."'"
);
}
Here is where you can find all the functions.
The funcion password_needs_rehash has been introduced to check if you need to upgrade:
password_needs_rehash($result_row->user_password_hash, PASSWORD_DEFAULT)
This function checks to see if the supplied hash implements the algorithm and options provided. If not, it is assumed that the hash needs to be rehashed.
If you have problems to understand what this function does, the RFC contains the function in PHP code. So if you can read PHP code, you should be able to read the following (see the part introduced as It could be implemented in user-land by:): https://wiki.php.net/rfc/password_hash#password_needs_rehash
Makes sense to test if the hash in the database (store) is of the same algorithm as in PASSWORD_DEFAULT or not. That means to check if PASSWORD_DEFAULT has been changed between the time the hash has been stored last time and now.
Right now PASSWORD_DEFAULT is PASSWORD_BCRYPT so it should always return false. In your case it returns true, because you're testing without your password options.
Change that and you should be fine:
$options = ['cost' => 12, 'salt' => 'superfreakingsonicdude',];
########
if (password_needs_rehash($result_row->user_password_hash, PASSWORD_DEFAULT, $options))
########
{
$newhash = password_hash($_POST['user_password'], PASSWORD_DEFAULT, $options);
################ ########
// update hash in database
$this->connection->query(
"UPDATE users SET user_password_hash='" . $newhash .
"' WHERE user_name='".$result_row->user_name."'"
);
}
Also consider to continue to use PASSWORD_DEFAULT if you want to benefit from a default hashing algo update in PHP core.
The input to the hash is the password and salt. Same password, same salt, same result.
If you leave the salt parameter out, a random salt will be generated each time and you should get a different result. You should not provide a static salt. This means all users have the same salt, which greatly diminishes its effectiveness. Each individual hash needs should have a random salt.
Related
Sorry for being new in php programming, in my old project I use MD5 to encrypt the password, however, it is not secure enough and I found some resource on the internet suggest using password salt instead.
The problem is , I am using codeigniter, is there any helper/ library for this purpose / how to change my old code to support the generation of the password salt?
Thanks for helping. I am using PHP 5.2
And here is the old code to validate, while the user account generate by storing the md5($password);
function validate_credentials() {
$this->load->model('Secure_model');
$username = $this->input->post('username');
$password = md5($this->input->post('password'));
$is_valid = $this->Secure_model->validate('customer', $username, $password);
if ($is_valid) {
$data = array(
'user_id' => $this->get_user_id($username),
'user_name' => $username,
'is_logged_in_user' => true
);
$this->session->set_userdata($data);
redirect('profile');
} else {
$data['message_error'] = TRUE;
$data['main_content'] = 'front/login';
$this->load->view('front/includes/template', $data);
}
}
If you are really stuck with PHP 5.2 your best bet will propably be the phpass library, because there is no PHP support of the BCrypt algorithm.
PHP versions 5.3 and later will have native support of BCrypt, so you can use the PHP function password_hash() to hash a password. There is a compatibility pack for versions before 5.5.
// 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);
In every case you are doing right with discarding MD5 and switching to another algorithm. Make sure that you use an algorithm with a cost factor like BCrypt or PBKDF2, fast algorithms like SHA* are not appropriate to hash passwords. Salting is mandatory, though the salt can be stored in the database, it fulfills its purpose even if it is known.
look this part of my code I use to register an user:
public function addUser($data){
$sql = "INSERT INTO `user` salt=" . $this->db->escape($salt = substr(md5(uniqid(rand(), true)), 0, 9)) .", password=".$this->db->escape(sha1($salt . sha1($salt . sha1($data['password'])))).".......";
$this_>db->query($sql);
The information of salt and password are stored in your user table.
To retrieve the information and validate the password you do this:
$query = $this->CI->db->query("SELECT * FROM `user` WHERE email =".$this->CI->db->escape($email)." AND password = SHA1(CONCAT(salt, SHA1(CONCAT(salt, SHA1(" . $this->CI->db->escape($password) . ")))))");
Here are some simple solutions.
You can use sha* hash functions , be careful in using md5 since it has a
higher rate of collisions than sha,
and also about your problem with salt, it is ok if you dont salt your
password, just make sure your users use a very good password with a combination of
lower and upper cases and with numbers and make them lengthy.
I would like to advise you to use bcrypt but since you are using 5.2 it has a bug on that version and certain password libs like PHPPASS and PHPLIB Cater Only to 5.3 and above. Best option is to upgrade to 5.3 so that you can use the php libs, but take care full caution the scripts.
As far as I know codeigniter does not have a built-in function for this...
To make a hash with PHP you need
the password
a true random salt
a slow hashing algorithm
By PHP your can create a true random salt by using mcrypt_create_iv().
To make the hash, you can use the crypt() or password_hash, which supports slow algorithms, like CRYPT_BLOWFISH. Forget md5, or sha1, they are too fast, so with the proper tool it is possible to find out passwords hashed by them.
$salt = mcrypt_create_iv(22, MCRYPT_DEV_URANDOM);
$hash = password_hash($password, PASSWORD_BCRYPT, array('cost' => 11, 'salt' => $salt));
The password_hash() function can generate a true random salt automatically, so you don't have to generate it manually if you don't want. The salt will be appended to the hash.
Sadly PHP 5.2 does not have CRYPT_BLOWFISH support. So you have to use the PHPASS lib.
You should set a $config['salt] = '$%#~De#';// in your config file
//Inside your model or controller where you are getting your post values
$password = sha1($this->config->item('salt').$this->input->post->('password')));
This should give you has password
As part of learning php, I wanted to try a register and login page, however, the site I'm following for how to store a password uses MySQLI and I'm not using that:
Hashing the password
$password1 = 'hello123';
// A higher "cost" is more secure but consumes more processing power
$cost = 10;
// Create a random salt
$salt = strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');
// Prefix information about the hash so PHP knows how to verify it later.
// "$2a$" Means we're using the Blowfish algorithm. The following two digits are the cost parameter.
$salt = sprintf("$2a$%02d$", $cost) . $salt;
// Value:
// $2a$10$eImiTXuWVxfM37uY4JANjQ==
// Hash the password with the salt
$hash = crypt($password1, $salt);
I'm stuck on retrieving the password however, here's the site's code for it:
$username = 'Admin';
$password = 'gf45_gdf#4hg';
$sth = $dbh->prepare('
SELECT
hash
FROM users
WHERE
username = :username
LIMIT 1
');
$sth->bindParam(':username', $username);
$sth->execute();
$user = $sth->fetch(PDO::FETCH_OBJ);
// Hashing the password with its hash as the salt returns the same hash
if ( crypt($password, $user->hash) === $user->hash ) {
// Ok!
}
From what I can see, he grabs the hash value of the password for the user in the DB and compares the password that was passed using the hash and check with the one in the DB.
I've been trying this but the result hash is never the same as the original one:
$pwdtocheck = 'hello123';
// no call do DB yet, doing this on the same page after hashing, the $hash is the same as above
$pwdhash = crypt($pwdtocheck, $hash);
// if I echo $pwdhash it's never exactly the same as the $hash.
if ( $pwdhash === $hash) {
echo "same pwd";
}
I cannot see the actual problem in your code, maybe your database field is smaller than 60 characters, or you are comparing different passwords. In every case there is an easier and safer way to hash passwords, just use the new functions password_hash() and password_verify(). There exists also a compatibility pack for earlier PHP versions.
// 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);
EDIT:
I see now that you are using a salt when you compare the passwords. In your line:
$pwdhash = crypt($pwdtocheck, $hash);
the $hash variable has the salt prepended to it because crypt() will automatically do that for you. crypt() will extract the salt from the $hash because it knows the expected length of the salt based on the algorithm used. See the documentation.
I'll keep my original answer below for context and for those looking for a similar answer.
END EDIT
The password is not the same for you because you are using a salt when you originally hash the password to put in your database, but you are not salting the password later when you check against the database.
You should use the same salt string when you save the password as when you check the user's password on login. Usually, you will randomly generate the salt string for each password (as you are doing) and then save the salt string to the database along with the hashed password (either in the same column or its own column) so that you can use the same salt to check the user's password on login.
See https://crackstation.net/hashing-security.htm#salt for reference.
I can't access the article you're referencing but I imagine:
You need to check using the salt as the salt and not the hash.
crypt($pwdtocheck, $user->salt) == $user->hash
should work
I'm using PHP's crypt function for password hashing/encryption, but I don't think I am doing it right because "nathan12" and "nathan123" both allow me to login to my account on my system (the actual password is "nathan123", and therefore "nathan12" or anything else should NOT allow me to login).
Here's what my system does when a user registers:
[...]
$salt = uniqid(mt_rand(), true);
$password = crypt($password, $salt); // '$password' is the inputted password
$insertUserStmt = $mysqli->prepare("INSERT INTO users (name,
username,
password,
password_salt,
email,
created) VALUES (?, ?, ?, ?, ?, ?)");
$insertUserStmt->bind_param("sssssi", $name, $username, $password, $salt, $email, time());
$insertUserStmt->execute();
[...]
It inserts the hashed/encrypted password ($password) into the database along with the $salt.
When someone tries to login, the following is done to check if the user has inputted the correct password for the username they inputted:
[...]
// $password_salt is from DB; $password is inputted password
$password_crypt = crypt($password, $password_salt);
// $login_password is from DB
if($password_crypt == $login_password) {
[...]
I'm probably not even using the crypt function properly, but according to the PHP docs the first parameter is a string (the password) and second is the salt.
The standard DES-based crypt() [...] only uses the first eight characters of str, so longer strings that start with the same eight characters will generate the same result (when the same salt is used).
source
Use a salt that starts with $<algo>$ to use something other than DES. See the crypt() documentation for details.
You should be using password_hash() instead of crypt, for the reasons you mention: "I'm probably not even using the crypt function properly". You say you are getting the salt from the DB... this sounds insecure. with password_hash() you can let PHP handle the salting for you in a secure manner.
More details on why this is superior:
http://www.sitepoint.com/hashing-passwords-php-5-5-password-hashing-api/
You should use more than just a password salt to encrypt passwords.
You can store a random string in your configuration file.
$config['passwordKey'] = 'asjdfa783#H$Khjsdfhas78a734J%JSDGK2348235hxmfdA';
And append it to $salt when encrypting. This way if the database is compromised, and your file system is not, then attackers can't decrypt your database password hashes. This should be essential to protect the users information on other sites with identical login information.
To hash your passwords, password_hash is a simple crypt() wrapper specially configured for password hashing!
(source)
$password = password_hash($password, PASSWORD_BCRYPT, array(
'cost' => 60,
'salt' => $salt . $config['passwordKey']
));
I'm using SHA-512 to encrypt passwords. I think that I'm doing everything correctly (and even duplicating code that is successful elsewhere), but every time I try to enter a password, it fails to match what I have stored in my database.
$sql = "SELECT hashed_password FROM Users WHERE user_name='$username'";
$result = mysql_query($sql);
$db_password = mysql_result($result, 0);
if (crypt($currentPlaintext, $db_password) != $db_password) {
echo "Your current password is incorrect.";
die();
}
I have verified the following obvious potential problems and eliminated them as causes:
1) There is an encrypted password stored in my database
2) I am able to retrieve this password and store it in the variable $db_password
3) The variables $username and $currentPlaintext do have the correct values as passed over by my ajax function call.
So, why isn't this working? Am I missing something obvious? Thanks!
EDIT: Thanks for the comments so far. To clarify, as I understand the crypt() function, the second argument of crypt is actually the entire string that is stored in the db. This string includes not only the hashed password, but also the algorithm, salt value, and number of rounds of hashing. The crypt() function is supposed to pull the salt value out of this string and then apply it to hash the first argument. The result is then compared to the previously hashed password. That's why this is so confusing. I don't have to give it a salt value - the salt is there in the database. Am I misunderstanding what is going on in this function?
I originally hashed the password with this code:
$salt = uniqid();
$algo = '6';
$rounds = '5000';
$cryptSalt = '$' . $algo . '$rounds=' . $rounds . '$' . $salt;
$hashedPassword = crypt($plaintext, $cryptSalt);
Are you sure you are using Sha512? Depending on the system crypt() might use a different algorithm.
You check if
crypt($plaintext, $pass) == $pass
Looking at the crypt manual you see that the second argument of crypt is $salt. So you need to give it the salt you used when you created the hash. If you do not use a salt (which you should be doing), then try to remove this second argument.
The crypt function in PHP doesn't necessarily return an SHA512 hash.
Use hash('sha512', $currentPlaintext) instead.
Like most users, I'm simply trying to figure out a secure way to store passwords. What I haven't found here (or maybe it's my lack of understanding) is how to retrieve a salted hash in my database and separate the salt from the hashed password, especially with unique salts to each password while maintaining the salt+password in a single column.
I'm finding all these cool ways to encrypt passwords (SHA-256, but does MySQL only support SHA/1 and MD5?) and other things from the PHP manual, but not sure how store and retrieve the passwords.
So, far this is all I understand:
SHA('$salt'.'$password') // My query sends the password and salt
// (Should the $salt be a hash itself?)
After that I'm lost with salts.
Retrieving the password without a salt is easy, but the salt confuses me. Where do I get the value from $salt again, especially if it's unique and secure? Do I hide them in another database? Constant (seems unsafe)?
EDIT: Is the key variable in HMAC supposed to be salt or is this something else?
First of all, your DBMS (MySQL) does not need to have any support for cryptographic hashes. You can do all of that on the PHP side, and that's also what you should do.
If you want to store salt and hash in the same column you need to concatenate them.
// the plaintext password
$password = (string) $_GET['password'];
// you'll want better RNG in reality
// make sure number is 4 chars long
$salt = str_pad((string) rand(1, 1000), 4, '0', STR_PAD_LEFT);
// you may want to use more measures here too
// concatenate hash with salt
$user_password = sha512($password . $salt) . $salt;
Now, if you want to verify a password you do:
// the plaintext password
$password = (string) $_GET['password'];
// the hash from the db
$user_password = $row['user_password'];
// extract the salt
// just cut off the last 4 chars
$salt = substr($user_password, -4);
$hash = substr($user_password, 0, -4);
// verify
if (sha512($password . $salt) == $hash) {
echo 'match';
}
You might want to take a look at phpass, which also uses this technique. It is a PHP hashing solution which uses salting amongst some other things.
You should definitely take a look at the answer to the question WolfOdrade linked to.
Personally I recommend letting MySQL do this with its built in functions.
They way I do this is to create a function in my database config file which returns a key string. The config file should be outside your sites root so that the webserver can access the file but not others. so for example:
function enc_key(){
return "aXfDs0DgssATa023GSEpxV";
}
Then in your script use it with the sql query and AES_ENCRYPT and AES_DECRYPT functions in MySQL like this:
require_once('dbconf.inc.php');
$key = enc_key();
//When creating a new user
$sql = "INSERT INTO users (username, password) VALUES ('bob', AES_ENCRYPT('{$key}', {$password}))";
//When retrieving users password
$sql = "SELECT AES_DECRYPT('{$key}', password) AS password FROM users WHERE username like 'bob'";