The new password hashing API exposes four simple functions:
password_hash() – used to hash the password.
password_verify() – used to verify a password against its hash.
password_needs_rehash() – used when a password needs to be rehashed.
password_get_info() – returns the name of the hashing algorithm and various options used while hashing.
Hash passwords supplied by users:
Password hashing is one of the most basic security considerations that must be made when designing any application that accepts passwords from users. Without hashing, any passwords that are stored in your
application's database can be stolen if the database is compromised, and then immediately used to compromise not only your application, but also the accounts of your users on other services, if they do not use unique passwords.
By applying a hashing algorithm to your user's passwords before storing them in your database, you make it implausible for any attacker to determine the original password, while still being able to compare the resulting hash to the original password in the future.
It is important to note, however, that hashing passwords only protects them from being compromised in your data store, but does not necessarily protect them from being intercepted by malicious code injected into your application itself.
password_hash()
Although the crypt() function is secure, it’s considered by many to be too complicated and prone to programmer error. Some developers then use a weak salt and weak algorithm for generating a hash instead, for example:
<?php
$hash = md5($password . $salt); // works, but dangerous
But the password_hash() function can simplify our lives and our code can be secure. When you need to hash a password, just feed it to the function and it will return the hash which you can store in your database.
<?php
$hash = password_hash($password, PASSWORD_DEFAULT);
That’s it! The first parameter is the password string that needs to be hashed and the second parameter specifies the algorithm that should be used for generating the hash.
The default algorithm is currently bcrypt, but a stronger algorithm may be added as the default later at some point in the future and may generate a larger string. If you are using PASSWORD_DEFAULT in your projects, be sure to store the hash in a column that’s capacity is beyond 60 characters. Setting the column size to 255 might be a good choice. You could also use PASSWORD_BCRYPT as the second parameter. In this case the result will always be 60 characters long.
The important thing here is that you don’t have to provide a salt value or a cost parameter. The new API will take care of all of that for you. And the salt is part of the hash, so you don’t have to store it separately. If you want to provide your own salt (or cost), you can do so by passing a third argument to the function, an array of options.
<?php
$options = [
'salt' => custom_function_for_salt(), //write your own code to generate a suitable salt
'cost' => 12 // the default cost is 10
];
$hash = password_hash($password, PASSWORD_DEFAULT, $options);
In this way, you are always up-to-date with new security measures. If PHP later decides to implement a more powerful hashing algorithm your code can take advantage of it.
password_verify()
Now that you have seen how to generate hashes with the new API, let’s see how to verify a password. Remember that you store the hashes in a database, but it’s the plain password that you get when a user logs in.
The password_verify() function takes a plain password and the hashed string as its two arguments. It returns true if the hash matches the specified password.
<?php
if (password_verify($password, $hash)) {
// Success!
}
else {
// Invalid credentials
}
Just remember that the salt is a part of the hashed password which is why we are not specifying it separately here.
password_needs_rehash()
What if you need to modify the salt and cost parameters for the hash strings? This is a concern as you might decide to improve security by adding a stronger salt or larger cost parameter. Moreover, PHP might change the default implementation of the hashing algorithm. In all of these cases, you would want to rehash the existing passwords.
It helps checks if the specified hash implements a particular algorithm and uses specific options like cost and salt while being created.
<?php
if (password_needs_rehash($hash, PASSWORD_DEFAULT, ['cost' => 12])) {
// the password needs to be rehashed as it was not generated with
// the current default algorithm or not created with the cost
// parameter 12
$hash = password_hash($password, PASSWORD_DEFAULT, ['cost' => 12]);
// don't forget to store the new hash!
}
Keep in mind that you’ll need to do this when the user tries to login to your website as this is only time you have access to the plain password.
password_get_info()
password_get_info() accepts a hash and returns an associative array of three elements:
algo – a constant that identifies a particular algorithm
algoName – the name of the algorithm used
options– various options used while generating the hash
The common hashing functions such as md5() and sha1() unsuitable for passwords
Hashing algorithms such as MD5, SHA1 and SHA256 are designed to be very fast and efficient. With modern techniques and computer equipment, it has become trivial to "brute force" the output of these algorithms, in order to determine the original input.
Because of how quickly a modern computer can "reverse" these hashing algorithms, many security professionals strongly suggest against their use for password hashing.
Hash the passwords, if the common hash functions are not suitable:
When hashing passwords, the two most important considerations are the computational expense, and the salt. The more computationally expensive the hashing algorithm, the longer it will take to brute force its output.
There are two functions that are bundled with PHP that can perform hashing using a specified algorithm.
The first hashing function is crypt(), which natively supports several hashing algorithms. When using this function, you are guaranteed that the algorithm you select is available, as PHP contains native implementations of each supported algorithm, in case one or more are not supported by your system.
The second hashing function is hash(), which supports many more algorithms and variants than crypt(), but does not support some algorithms that crypt() does. The Hash extension is bundled with PHP, but can be disabled during compile-time, so it is not guaranteed to be available, while crypt() is, being in the PHP core.
The suggested algorithm to use when hashing passwords is Blow fish, as it is significantly more computationally expensive than MD5 or SHA1, while still being scalable.