PHP Password Encryption Security
PHP Password Encryption Security
/* User's password. */
$password = 'my secret password';
/* User's password. */
$password = 'my secret password';
The result hash from password_hash() is secure because:
It uses a strong hashing algorithm.
It adds a random salt to prevent rainbow tables and dictionary
attacks.
Once you have the password hash, you can save it directly in the
database.
Let’s see how with the next examples.
How to use password_hash()
First, you need a database users table.
For example, let’s use a simplified version of the “accounts” table from
my Authentication Tutorial.
This table has the following columns:
account_id: the unique identifier of the account.
account_name: the account username.
account_passwd: the password hash.
This is the SQL code to create the table (you can use it with
PhpMyAdmin to create the table on your development environment):
Important:
Be sure to set the password column as a varchar.
(A varchar is a text column of variable length.)
The reason is that the size of the hash from password_hash() can
change (more details on this later).
If you need help with SQL, you can find all you need here: How to use
PHP with MySQL
Now, you need to connect to the database from your PHP script.
If you don’t know how, here is a simple PDO connection script you can
use right away.
Just edit the connection parameters to make it work with your own
environment:
Now you are ready to add a new user to the table.
Here is a full example (pdo.php is the script containing the previous
database connection snippet):
/* Username. */
$username = 'John';
/* Password. */
$password = 'my secret password';
Important:
In this example we skipped the validation steps, including:
checking the username and password length
checking for invalid characters
checking if the username already exists
And so on.
Validation is out of the scope of this tutorial, but remember that you
always need to validate your input variables.
You can refer to my Login and Authentication Tutorial for more details
and examples.
If you want to learn more about PHP security, take a look at my PHP
Security course.
How to change a user’s password
The next example shows how to change the password of an existing
user.
First, get the new password and create its hash with password_hash():
/* New password. */
$password = $_POST['password'];
Then, update the table row having the same account ID of the current
user and set the new hash.
Note: we assume the $accountId variable contains the account ID.
/* Include the database connection script. */
include 'pdo.php';
$row = $res->fetch(PDO::FETCH_ASSOC);
Important:
You cannot just compare two different hashes to see if they match.
The reason is that password_hash() creates salted hashes.
Salted hashes include a random string, named “salt”, as a protection
against rainbow tables and dictionary attacks.
Therefore, every hash will be different even if the source password is the
same.
Try the following code. You will see that the two hashes are different,
even if the password is the same:
Note:
password_verify() only works with hashes created
by password_hash().
You cannot use it to check a password against a MD5 or SHA hash.
How to increase hash security
The hash generated by password_hash() is very secure.
But you can make it even stronger with two simple techniques:
1. Increasing the Bcrypt cost.
2. Automatically updating the hashing algorithm.
Bcrypt cost
Bcrypt is the current default hashing algorithm used
by password_hash().
This algorithm takes an option parameter named “cost”. The default
cost value is 10.
By increasing the cost, you can make the hash more difficult to
compute. The higher the cost, the longer the time needed to create the
hash.
A higher cost makes more difficult to break the hash. However, it also
makes the hash creation and check longer, too.
So, you want to find a compromise between security and server load.
This is how you can set a custom cost value for password_hash():
/* Password. */
$password = 'my secret password';
But what cost value should you set?
A good compromise is a cost value that lets your server create the hash
in about 100ms.
Here is a simple test to find this value:
/* 100 ms. */
$time = 0.1;
/* Initial cost. */
$cost = 10;
Once you have found your cost, you can use it every time you
execute password_hash() like in the previous example.
Keeping your hashes up to date
with password_needs_rehash()
To understand this step, let’s see how password_hash() works.
password_hash() takes three arguments:
1. The password you need to hash
2. The hashing algorithm you want to use
3. An array of options to pass to the hashing algorithm
PHP supports different hashing algorithms, but you usually want to use
the default one.
You can select the default algorithm by using
the PASSWORD_DEFAULT constant, as you have seen in the previous
examples.
As of June 2020, the default algorithm is Bcrypt.
However, PHP can change the default algorithm in the future, if a better
and more secure algorithm is implemented.
When that happens, the PASSWORD_DEFAULT constant will point to
the new algorithm. So, all the new hashes will be created using the new
algorithm.
But what if you want to take all your old hashes, made with the previous
algorithm, and automatically create them again with the new one?
This is where password_needs_rehash() comes into play.
This function checks if a hash has been created with a given algorithm
and parameters.
For example:
/* Password. */
$password = 'my secret password';
$row = $res->fetch(PDO::FETCH_ASSOC);
try
{
$res = $pdo->prepare($query);
$res->execute($values);
}
catch (PDOException $e)
{
/* Query error. */
echo 'Query error.';
die();
}
}
}
}
$row = $res->fetch(PDO::FETCH_ASSOC);
try
{
$res = $pdo->prepare($query);
$res->execute($values);
}
catch (PDOException $e)
{
/* Query error. */
echo 'Query error.';
die();
}
}
}
}
Conclusion
In this tutorial you learned how to
use password_hash() and password_verify() to create secure hashes
of your passwords (and why you should not use MD5).
You also learned how to make your password hashes more secure by
setting a proper Bcrypt cost and automatically rehashing your
passwords as needed.
Now it’s your turn: leave your questions and your comments below.
P.S.
Do you want to get my exclusive PHP tips and my 7-step Guide to
improve your PHP skills? Enter your email below: