PHP secure hash generator

Posted on

Problem

As I understand it, blowfish (actually eksblowfish, as it’s used one way) is generally seen a secure hashing algorithm, even for enterprise use (correct me if I’m wrong). Because of this, I created functions to create and check secure password hashes using this algorithm, and using the (also deemed cryptographically secure) openssl_random_pseudo_bytes function to generate the salt.

<?php
/*
 * Generate a secure hash for a given password. The cost is passed
 * to the blowfish algorithm. Check the PHP manual page for crypt to
 * find more information about this setting. Returns null on failure.
 */
function generate_hash($password, $cost=11){
    /* To generate the salt, first generate enough random bytes. Because
     * base64 returns one character for each 6 bits, the we should generate
     * at least 22*6/8=16.5 bytes, so we generate 17. Then we get the first
     * 22 base64 characters
     */
    $salt=substr(base64_encode(openssl_random_pseudo_bytes(17,$secure)),0,22);
    /* If the random generation was not secure enough, do not continue
     */
    if(!$secure) return null;
    /* As blowfish takes a salt with the alphabet ./A-Za-z0-9 we have to
     * replace any '+' in the base64 string with '.'. We don't have to do
     * anything about the '=', as this only occurs when the b64 string is
     * padded, which is always after the first 22 characters.
     */
    $salt=str_replace("+",".",$salt);
    /* Next, create a string that will be passed to crypt, containing all
     * of the settings, separated by dollar signs
     */
    $param='$'.implode('$',array(
            "2y", //select the most secure version of blowfish (>=PHP 5.3.7)
            str_pad($cost,2,"0",STR_PAD_LEFT), //add the cost in two digits
            $salt //add the salt
    ));

    /* Now do the actual hashing
     */
    $hash=crypt($password,$param);
    if(strlen($hash)!=60) return null;
    return $hash;
}

/*
 * Check the password against a hash generated by the generate_hash
 * function.
 */
function validate_pw($password, $hash){
    /* Regenerating the with an available hash as the options parameter should
     * produce the same hash if the same password is passed.
     */
    return crypt($password, $hash)===$hash;
}
?>

Solution

I would have to agree with @EliasVanOotegem. Consider using password_hash() and password_verify(). All you’ve done is recreate something, which we know shouldn’t be done.

Documentation for the former and latter.

Leave a Reply

Your email address will not be published. Required fields are marked *