Zend PHP 5 – Security – Password Hashing API

When storing passwords, the golden rule is to not store them in plain text. PHP provides several functions to hash the passwords.

md5() and sha1()

Hashing algorithms such as MD5 and SHA1 are very fast and efficient. Unfortunately with modern computers, it has become trivial to “brute force” the output of these algorithms to determine the original input.

Because of how quickly a modern computer can “reverse” these hashing algorithms, it is recommended to not use these hashing algorithms. Recommended functions include crypt() and password_hash()

Crypt

string crypt ( string $str [, string $salt ] )

crypt() will return a hashed string using the standard Unix DES-based algorithm or alternative algorithms that may be available on the system.

The salt parameter is optional. However, crypt() creates a weak password without the salt. PHP 5.6 or later raise an E_NOTICE error without it. It is strongly advise to specify a salt for improved security.

Password Hash

As of PHP 5.5, password_hash() creates a new password hash using a strong one-way hashing algorithm.

$password = "dY^543U%?%$";
$password = password_hash($password, PASSWORD_DEFAULT);

The second parameter passed in the function is the algorithm. The following algorithms are supported

  • PASSWORD_DEFAULT – Uses the bcrypt algorithm (as of PHP 5.5). Note that this constant is designed to change over time as new and stronger algorithms are added to PHP. For that reason, the length of the result from using this identifier can change over time. Therefore, it is recommended to store the result in a database column that can expand beyond 60 characters (255 characters would be a good choice).
  • PASSWORD_BCRYPT – Use the CRYPT_BLOWFISH algorithm to create the hash. This will produce a standard crypt() compatible hash using the “$2y$” identifier. The result will always be a 60 character string, or FALSE on failure.

The options supported for the PASSWORD_BCRYPT algorithm are:

  • salt – The salt used manually will override the default salt that gets generated
  • cost – Denotes the algorithmic cost that should be used. If omitted, the default value of 10 is used

An example of manually setting the salt and the cost can be seen below:

$options = [
    'cost' => 11,
    'salt' => mcrypt_create_iv(22, MCRYPT_DEV_URANDOM),
];
echo password_hash("somepassword", PASSWORD_BCRYPT, $options);

The salt option has been deprecated as of PHP 7.0.0. It is now preferred to simply use the salt that is generated by default.

You can verify the hash using the password_verify() function.

$hash = password_hash("somepassword", PASSWORD_DEFAULT);
if (password_verify('somepassword', $hash)) {
    echo 'Password is valid.';
} else {
    echo 'Invalid password.';
}

You can retrieve the details of the password hash using the password_get_info() function:

$password = "somepassword";
$hash = password_hash($password, PASSWORD_DEFAULT);

print_r(password_get_info($hash));
// Prints: Array ( [algo] => 1 [algoName] => bcrypt [options] => Array ( [cost] => 10 ) )

The password_needs_rehash() 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.

The algorithm used by PASSWORD_DEFAULT is subject to change as different versions of PHP are released, so this function would be used here.

$password = 'somepassword';
$hash = '$2y$10$YCFsG6elYca568hBi2pZ0.3LDL5wjgxct1N8w/oLR/jfHsiQwCqTS';

// The cost parameter can change over time as hardware improves
$options = array('cost' => 11);

// Verify stored hash against plain-text password
if (password_verify($password, $hash)) {
    // Check if a newer hashing algorithm is available
    // or the cost has changed
    if (password_needs_rehash($hash, PASSWORD_DEFAULT, $options)) {
        // If so, create a new hash, and replace the old one
        $newHash = password_hash($password, PASSWORD_DEFAULT, $options);
    }

    // Log user in
}

Note: This article is based on PHP version 5.5.