1
\$\begingroup\$

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;
}
?>
\$\endgroup\$
  • 3
    \$\begingroup\$ Why bother reinventing the wheel? Time and energy best spent upgrading your PHP version to 5.5, which introduced the password_hash function, to be called like so: password_hash($password, PASSWORD_BCRYPT, array('salt' => 'yourSalt', 'cost' => 15)); \$\endgroup\$ – Elias Van Ootegem Jan 8 '14 at 7:11
  • \$\begingroup\$ Or even use something like phpass openwall.com/phpass . I feel that it is better to use something tested than roll your own (which may include vulnerabilities) \$\endgroup\$ – Sam Jan 8 '14 at 10:46
  • 1
    \$\begingroup\$ If PHP 5.5 isn't available, ircmaxell has made a drop in PHP version that just requires PHP 5.3: github.com/ircmaxell/password_compat \$\endgroup\$ – Corbin Feb 19 '14 at 19:08
3
\$\begingroup\$

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.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.