Question

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?

Was it helpful?

Solution

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

  • $salt could be false, mcrypt_create_iv returns false on error (unlikely because of the hash output differences you mentioned above, but worth checking.
  • Output characters are not recognized as you suspect. You could try converting the database column types to varbinary and using a string to binary or hex decoder before adding to your prepare.
  • Try experimenting with column types with different character encodings and see what column type your salts can go into. UTF-8 uses a variable number of bytes for each character, which makes me uncomfortable when dealing with things that are absolutes. A salt and a hash are generally considered to be fixed-with bit fields, often expressed in hex format for convenience.

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top