Zakro Gogolidze

PHP Password Hashing

PHP Password Hashing

Eventually everyone builds a PHP application that relies on user login. Usernames and passwords are stored in a database and later used to authenticate users upon login.

It is important that you properly hash passwords before storing them. Hashing and encrypting are two very different things that often get confused.

Hashing is an irreversible, one-way function. This produces a fixed-length string that cannot be feasibly reversed. This means you can compare a hash against another to determine if they both came from the same source string, but you cannot determine the original string. If passwords are not hashed and your database is accessed by an unauthorized third-party, all user accounts are now compromised.

Unlike hashing, encryption is reversible (provided you have the key). Encryption is useful in other areas, but is a poor strategy for securely storing passwords.

Passwords should also be individually salted by adding a random string to each password before hashing. This prevents dictionary attacks and the use of “rainbow tables” (a reverse list of crytographic hashes for common passwords.)

Hashing and salting are vital as often users use the same password for multiple services and password quality can be poor.

Additionally, you should use a specialized password hashing algorithm rather than fast, general-purpose cryptographic hash function (e.g. SHA256). The short list of acceptable password hashing algorithms (as of June 2018) to use are:

  • Argon2 (available in PHP 7.2 and newer)
  • Scrypt
  • Bcrypt (PHP provides this one for you; see below)
  • PBKDF2 with HMAC-SHA256 or HMAC-SHA512

Fortunately, nowadays PHP makes this easy.

Hashing passwords with password_hash

In PHP 5.5 password_hash() was introduced. At this time it is using BCrypt, the strongest algorithm currently supported by PHP. It will be updated in the future to support more algorithms as needed though. The password_compat library was created to provide forward compatibility for PHP >= 5.3.7.

Below we hash a string, and then check the hash against a new string. Because our two source strings are different (‘secret-password’ vs. ‘bad-password’) this login will fail.

<?php
require 'password.php';

$passwordHash = password_hash('secret-password', PASSWORD_DEFAULT);

if (password_verify('bad-password', $passwordHash)) {
// Correct Password
} else {
// Wrong password
}

password_hash() takes care of password salting for you. The salt is stored, along with the algorithm and “cost”, as part of the hash. password_verify() extracts this to determine how to check the password, so you don’t need a separate database field to store your salts.

Handcrafted by me © 2020

Made with with Django & Python