Skip to Content
Course content

65: Password Hashing with password_hash

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was reviewing a codebase for a startup that had been built by a very talented, but slightly outdated, developer. He had a "security" strategy for passwords that looked like this: md5($password . "s3cret_s@lt_123"). He felt great about it because he'd added a "salt"—a random string to prevent rainbow table attacks. The problem? That salt was hardcoded in the source code. When the database was eventually leaked in a minor breach, the attacker found the salt in a backup of the PHP files, and the entire user table was cracked in under an hour. MD5 is just too fast; a modern GPU can chew through billions of MD5 hashes per second.

This is why we stop trying to be clever with our own hashing logic. In modern PHP, you should never use md5(), sha1(), or even hash() for passwords. Instead, we use a dedicated API designed specifically to be slow and computationally expensive, which is exactly what you want when defending against brute-force attacks.

The Magic of password_hash()

The password_hash() function is the industry standard for a reason. It doesn't just scramble the string; it handles the salting for you automatically. You don't need to create a salt column in your database or store a secret string in your config file. The salt is generated randomly for every single password and is bundled directly into the resulting hash string.

$password = "my_super_secret_123";
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);

echo $hashedPassword; 
// Output looks something like: $2y$10$nOUIs5kJWOnp7v9sS6S.u.GfS2S8KkFfLd/R7P8V.h6R...

Notice that PASSWORD_DEFAULT. I always recommend using this because it tells PHP to use the strongest algorithm currently supported by your version of PHP (usually Bcrypt or Argon2). If PHP is updated in the future to a stronger algorithm, your code stays current without you having to manually change the algorithm constant everywhere.

Verification and the Cost Factor

Since the salt is baked into the hash, you can't just hash the user's input during login and compare two strings with ==. That's a common mistake. Instead, you use password_verify(). This function knows how to extract the salt from the stored hash, apply it to the plain-text password provided by the user, and tell you if they match.

$userInput = "my_super_secret_123";
$storedHash = '$2y$10$nOUIs5kJWOnp7v9sS6S.u.GfS2S8KkFfLd/R7P8V.h6R...';

if (password_verify($userInput, $storedHash)) {
    echo "Access granted!";
} else {
    echo "Invalid password.";
}

One thing to keep in mind: these functions are intentionally slow. That's a feature, not a bug. If you're running this on a very powerful server or targeting extremely high security, you can pass an options array as the third argument to password_hash() to increase the "cost." The cost determines how many iterations the hashing algorithm runs. Increasing the cost by 1 doubles the time it takes to calculate the hash. Just be careful—set it too high, and you'll accidentally create a Denial of Service (DoS) vulnerability where a few login attempts peg your CPU at 100%.




📋 Practical Task

Exercise: Building a Secure User Credential Validator

Your task is to create a small script that simulates a user registration and login flow. You don't need a real database for this—you can use an associative array to simulate one.

  • Create an array called $user_db where the keys are usernames and the values are the hashed passwords.
  • Write a function called registerUser($username, $password) that hashes the password using PASSWORD_DEFAULT and adds the user to your array.
  • Write a function called loginUser($username, $password) that checks if the username exists in the array and uses password_verify() to check if the password is correct.
  • Test your system by registering a user, then attempting to log in with both the correct password and a wrong one to verify your logic works.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.