Reputation: 24112
I have been trying to make my users passwords really secure using pbkdf2.
The password hash goes into the database fine, but the salt is not.
It seems the salt contains exotic characters that the mysql column doesnt like.
All columns in my 'users' table are UTF8_unicode_ci.
Here is my password hasher:
$size = mcrypt_get_iv_size(MCRYPT_CAST_256, MCRYPT_MODE_CFB);
$salt = mcrypt_create_iv($size, MCRYPT_DEV_RANDOM);
$passHash = pbkdf2('SHA512', $pass, $salt, 8192, 256) ;
include("dbconnect.php") ;
$result = $dbh->prepare("INSERT INTO users (name, email, qq, password, salt)VALUES(?, ?, ?, ?, ?)") ;
$result->bindParam(1, $name, PDO::PARAM_STR) ;
$result->bindParam(2, $email, PDO::PARAM_STR) ;
$result->bindParam(3, $qq, PDO::PARAM_STR) ;
$result->bindParam(4, $passHash, PDO::PARAM_STR) ;
$result->bindParam(5, $salt, PDO::PARAM_STR) ;
$result->execute() ;
And the pbkdf2:
/*
* PBKDF2 key derivation function as defined by RSA's PKCS #5: https://www.ietf.org/rfc/rfc2898.txt
* $algorithm - The hash algorithm to use. Recommended: SHA256
* $password - The password.
* $salt - A salt that is unique to the password.
* $count - Iteration count. Higher is better, but slower. Recommended: At least 1000.
* $key_length - The length of the derived key in bytes.
* $raw_output - If true, the key is returned in raw binary format. Hex encoded otherwise.
* Returns: A $key_length-byte key derived from the password and salt.
*
* Test vectors can be found here: https://www.ietf.org/rfc/rfc6070.txt
*
* This implementation of PBKDF2 was originally created by https://defuse.ca
* With improvements by http://www.variations-of-shadow.com
*/
function pbkdf2($algorithm, $password, $salt, $count, $key_length, $raw_output = false){
$algorithm = strtolower($algorithm);
if(!in_array($algorithm, hash_algos(), true))
die('PBKDF2 ERROR: Invalid hash algorithm.');
if($count <= 0 || $key_length <= 0)
die('PBKDF2 ERROR: Invalid parameters.');
$hash_length = strlen(hash($algorithm, "", true));
$block_count = ceil($key_length / $hash_length);
$output = "";
for($i = 1; $i <= $block_count; $i++) {
// $i encoded as 4 bytes, big endian.
$last = $salt . pack("N", $i);
// first iteration
$last = $xorsum = hash_hmac($algorithm, $last, $password, true);
// perform the other $count - 1 iterations
for ($j = 1; $j < $count; $j++) {
$xorsum ^= ($last = hash_hmac($algorithm, $last, $password, true));
}
$output .= $xorsum;
}
if($raw_output)
return substr($output, 0, $key_length);
else
return bin2hex(substr($output, 0, $key_length));
}
Also, I have just noticed that it is storing totally different hashes for passwords that are the same.
Am I doing this right?
Upvotes: 2
Views: 4884
Reputation: 11889
You should convert the result into base64 encoding before storing to a varchar column. Base64 encoding basically converts an array of bytes into something in the ASCII range of displayable (and therefore SQL storable) characters.
Upvotes: 1
Reputation: 10989
It's hard to give an exact answer to a question as broad as 'Am I doing this right?' I can say that the entire point of salting passwords before hashing is so that the resultant hash will be unique between users. So getting different output for the same input is a good thing. Look up 'dictionary attack' for more information on why.
As to your code, it sounds like what you really want to know is why your salt isn't getting stored to the database. Debugging steps I can think of, without more specific details
I might be able to narrow down a problem if you provided your server environment, php version, mysql version etc. and a few samples of salts which aren't being stored correctly.
Upvotes: 1