How to Secure Passwords with password_hash in PHP

Storing user passwords the right way is one of the most critical security aspects in web development. Many beginner developers store passwords in plain text or use old hash functio...

How to Secure Passwords with password_hash in PHP

Storing user passwords the right way is one of the most critical security aspects in web development. Many beginner developers store passwords in plain text or use old hash functions like MD5 and SHA1, which are no longer safe. PHP provides the built-in functions password_hash() and password_verify(), designed specifically for this purpose and very easy to use.

Why Are MD5 and SHA1 Unsafe for Passwords?

MD5 and SHA1 are general cryptographic hash functions designed for speed. For passwords, that's actually dangerous because:

  • Too fast — an attacker can try billions of combinations per second (brute force).
  • Deterministic without salt — the same hash for the same password, vulnerable to a rainbow table attack.
  • Already considered cryptographically weak.

The password_hash() function uses an algorithm like Bcrypt by default that is deliberately designed to be slow and automatically adds a unique salt.

Using password_hash()

Using password_hash() is very simple:

<?php

$originalPassword = "MySecret123!";

// Create a password hash with the bcrypt algorithm (default)
$hash = password_hash($originalPassword, PASSWORD_DEFAULT);

echo $hash;
// Example output: $2y$10$abcdefghijklmnopqrstu.hashedpasswordstring
// A new hash every time it's called even for the same password!

Some important things about password_hash():

  • PASSWORD_DEFAULT — uses the best algorithm available in the current PHP version (bcrypt in PHP 8).
  • The generated hash is always different even if the password is the same, because the salt is generated randomly each time.
  • Store the hash result in a database column of type VARCHAR(255) to accommodate future length changes.

Verifying a Password with password_verify()

To check whether the password entered by the user matches the stored hash:

<?php

$hashFromDatabase = '$2y$10$abcdefghijklmnopqrstu.hashedpasswordstring';
$passwordInput    = "MySecret123!";
$wrongPassword    = "anotherpassword";

if (password_verify($passwordInput, $hashFromDatabase)) {
    echo "Password correct! Login successful.";
} else {
    echo "Wrong password!";
}

// password_verify() is safe from timing attacks
// because it always takes the same amount of time to compare

Don't compare hashes manually with the == or === operator. Always use password_verify(), which is designed to be safe from timing attacks.

Complete Implementation: Registration and Login

Here is a user registration implementation that stores the password securely:

<?php
// register.php
require 'db.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username']);
    $email    = trim($_POST['email']);
    $password = $_POST['password'];
    $confirm  = $_POST['confirm_password'];

    // Basic validation
    if (strlen($password) < 8) {
        $error = "Password must be at least 8 characters.";
    } elseif ($password !== $confirm) {
        $error = "Password and confirmation don't match.";
    } else {
        // Hash the password before storing it
        $hash = password_hash($password, PASSWORD_DEFAULT);

        $stmt = $pdo->prepare(
            "INSERT INTO users (username, email, password) VALUES (:u, :e, :p)"
        );
        try {
            $stmt->execute([':u' => $username, ':e' => $email, ':p' => $hash]);
            $success = "Registration successful! Please log in.";
        } catch (PDOException $e) {
            $error = "Username or email is already in use.";
        }
    }
}

And the login process that verifies the password:

<?php
// login.php
session_start();
require 'db.php';

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $username = trim($_POST['username']);
    $password = $_POST['password'];

    $stmt = $pdo->prepare("SELECT id, username, password FROM users WHERE username = :u LIMIT 1");
    $stmt->execute([':u' => $username]);
    $user = $stmt->fetch();

    // Verify: check that the user exists AND the password matches
    if ($user && password_verify($password, $user['password'])) {

        // Check whether the hash needs to be upgraded to a new algorithm
        if (password_needs_rehash($user['password'], PASSWORD_DEFAULT)) {
            $newHash = password_hash($password, PASSWORD_DEFAULT);
            $update = $pdo->prepare("UPDATE users SET password = :p WHERE id = :id");
            $update->execute([':p' => $newHash, ':id' => $user['id']]);
        }

        session_regenerate_id(true);
        $_SESSION['user_id']  = $user['id'];
        $_SESSION['username'] = $user['username'];
        header('Location: dashboard.php');
        exit;
    } else {
        $error = "Wrong username or password.";
    }
}

The password_needs_rehash() Function

This function is very useful when you update the algorithm or cost factor. When a user logs in successfully, check whether their hash needs updating and store the new one automatically — without needing to ask the user to change their password.

Setting the Bcrypt Cost Factor

The cost factor determines how slow the hashing process is. The higher the value, the more secure but the slower. The default is 10; a value of 12 is good enough for most applications:

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

Conclusion

Securing passwords in PHP is very easy with password_hash() and password_verify(). There's no reason to use MD5, SHA1, or store passwords in plain text. Always hash the password before storing it in the database, verify with password_verify(), and take advantage of password_needs_rehash() to keep the hash up to date.

password_hash php password_verify php hash password php keamanan password php bcrypt php php native keamanan
Share this article
Back to Blog
🚀 Partner Recommendation

Need Premium Source Code & Business Apps?

Access Laravel applications, POS systems, School Management, Clinic Software, ERP solutions, and ready-to-use premium source code at GudangCode.

GudangCode
  • ✔ Premium Source Code
  • ✔ Ready-to-Use Systems
  • ✔ Lifetime Updates
  • ✔ Lifetime Membership
  • ✔ Daily App Updates
Join Membership →