How To Use Bcrypt For Password Hashing In PHP?

Published October 2, 2024

Problem: Secure Password Hashing in PHP

Storing passwords securely is important for web application development. PHP developers need a good method to hash passwords to protect user data from breaches. Bcrypt offers a solution for password hashing, but using it correctly requires specific knowledge and steps.

Implementing Bcrypt in PHP

Using PHP 5.5 and Above

PHP 5.5 and newer versions include built-in functions for password hashing using Bcrypt. The main function is password_hash(), which creates a Bcrypt hash of a password.

Here's how to use password_hash():

$password = 'user_password';
$hash = password_hash($password, PASSWORD_BCRYPT);

You can specify options, such as the cost factor:

$options = ['cost' => 12];
$hash = password_hash($password, PASSWORD_BCRYPT, $options);

To verify a password against a stored hash, use the password_verify() function:

$password = 'user_password';
$stored_hash = ''; // Retrieve the stored hash from your database

if (password_verify($password, $stored_hash)) {
    echo 'Password is correct';
} else {
    echo 'Password is incorrect';
}

Tip: Choosing the Right Cost Factor

When using Bcrypt, it's important to choose an appropriate cost factor. A higher cost factor makes the hashing process slower and more secure, but it also increases server load. Start with a cost of 10 or 12, and adjust based on your server's performance. You can use a benchmark script to find the optimal cost for your system.

Alternative for PHP Versions 5.3.7 to 5.5

For PHP versions 5.3.7 to 5.5, you can use a compatibility library that provides the same functionality as the built-in functions.

To install the library, use Composer. Add this to your composer.json file:

{
    "require": {
        "ircmaxell/password-compat": "^1.0"
    }
}

Then run composer install to install the library.

After installation, include the library in your PHP script:

require 'vendor/autoload.php';

You can now use password_hash() and password_verify() functions as if you were using PHP 5.5 or above:

$password = 'user_password';
$hash = password_hash($password, PASSWORD_BCRYPT);

if (password_verify($password, $hash)) {
    echo 'Password is correct';
} else {
    echo 'Password is incorrect';
}

This compatibility library allows you to use the same code for both older and newer PHP versions, making it easier to upgrade your application in the future.

Best Practices for Bcrypt Implementation

Choosing the Right Cost Factor

The cost factor in Bcrypt sets the number of iterations in the hashing process. A higher cost factor makes hashing slower and more resistant to brute-force attacks.

To select a suitable cost:

  1. Start with a cost of 10 as a baseline.
  2. Test your server to find the highest cost that doesn't cause delays.
  3. Aim for a hashing time of about 250 milliseconds per password.

Here's a benchmark script:

$timeTarget = 0.25; // 250 milliseconds

$cost = 8;
do {
    $cost++;
    $start = microtime(true);
    password_hash("test", PASSWORD_BCRYPT, ["cost" => $cost]);
    $end = microtime(true);
} while (($end - $start) < $timeTarget);

echo "Appropriate cost found: " . $cost;

Balance security and performance by choosing a cost that provides strong security without impacting your application's speed.

Tip: Regular Cost Factor Review

Review and adjust your Bcrypt cost factor regularly, as computing power increases over time. A good practice is to reassess the cost factor annually or when upgrading your server hardware.

Handling Password Storage and Verification

To store Bcrypt hashes:

  1. Use a column in your database to store the full hash string.
  2. Set the column type to VARCHAR(60) to fit the full Bcrypt hash.

For password verification:

  1. Get the stored hash from the database.
  2. Use the password_verify() function to compare the user's password with the stored hash.

Example:

$stored_hash = // Get from database
$user_password = $_POST['password'];

if (password_verify($user_password, $stored_hash)) {
    // Password is correct
} else {
    // Password is incorrect
}

When users change passwords:

  1. Generate a new hash for the new password.
  2. Update the stored hash in the database.

It's good to check if the hash needs rehashing:

if (password_needs_rehash($stored_hash, PASSWORD_BCRYPT)) {
    $new_hash = password_hash($user_password, PASSWORD_BCRYPT);
    // Update the stored hash in the database
}

This lets you update the cost factor or switch to a new hashing algorithm later without making users change their passwords.