Password Hashing and Verification in PHP
Introduction
In today's digital world, securing user data—especially passwords—is more important than ever. One of the most essential practices in web development is password hashing and verification. If you're storing passwords in your PHP application without hashing, you're putting user data and your entire application at risk.
This guide will teach you everything you need to know about secure password hashing in PHP, including how to hash passwords, how to verify them, and best practices for storing and handling credentials in a modern, secure PHP application.
What Is Password Hashing?
Password hashing is a one-way encryption process that transforms a plain-text password into a fixed-length string of characters, which is practically impossible to reverse. Unlike encryption, which can be decrypted with a key, a hash cannot be reversed—making it ideal for password storage.
Why Not Store Plain Text Passwords?
Storing passwords as plain text is dangerous because:
- Hackers can instantly gain access to all accounts if the database is breached.
- Passwords are often reused across multiple platforms.
- It violates security compliance standards like GDPR, PCI-DSS, etc.
Evolution of Password Hashing in PHP
md5() and sha1() — Do Not Use
Functions like md5() or sha1() were once common, but are now considered insecure due to fast hashing speed and vulnerability to rainbow table attacks.
$insecure_hash = md5($password); // Not safe
password_hash() and password_verify() — Modern and Secure
Introduced in PHP 5.5, the password_hash() function uses bcrypt by default, offering automatic salt generation and strong security.
PHP Password Hashing with password_hash()
Syntax:
string password_hash(string $password, int $algo, array $options = [])
Supported Algorithms:
- PASSWORD_DEFAULT — uses the current strongest algorithm (bcrypt as of PHP 8.0)
- PASSWORD_BCRYPT — bcrypt algorithm (60 characters)
- PASSWORD_ARGON2I and PASSWORD_ARGON2ID — added in PHP 7.2+, even stronger (memory-hard)
Example: Hash a Password Using Bcrypt
$password = "userSecurePassword123!";
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
echo $hashedPassword;
Output is a 60-character hashed string with auto-generated salt.
How Password Hashing Works Internally
- Salting: Adds a random value to each password before hashing.
- One-way: Cannot be reversed.
- Slow Algorithm: Makes brute-force attacks computationally expensive.
Example Hash Output:
$2y$10$CwTycUXWue0Thq9StjUM0uJ8rkTnQo3L4eYJGZzZZ6zEHEBK5z70u
- $2y$10$: Bcrypt version and cost factor.
- CwTycUXWue0Thq9StjUM0u: Salt.
- The rest: The hash.
Password Verification in PHP
Use password_verify() to compare a plain-text password with its hashed version.
Syntax:
bool password_verify(string $password, string $hash)
Example:
$enteredPassword = $_POST['password'];
$storedHash = '$2y$10$CwTycUXWue0Thq9StjUM0uJ8rkTnQo3L4eYJGZzZZ6zEHEBK5z70u';
if (password_verify($enteredPassword, $storedHash)) {
echo "Password is correct!";
} else {
echo "Invalid password.";
}
Safe against timing attacks and doesn't expose password data.
PHP Login System Example with Hashed Passwords
1. Register User and Hash Password
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = $_POST["username"];
$password = password_hash($_POST["password"], PASSWORD_DEFAULT);
// Store $username and $password in your database securely
}
2. Login and Verify Password
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = $_POST["username"];
$enteredPassword = $_POST["password"];
// Retrieve $storedHash from DB by $username
$stmt = $pdo->prepare("SELECT password FROM users WHERE username = ?");
$stmt->execute([$username]);
$storedHash = $stmt->fetchColumn();
if (password_verify($enteredPassword, $storedHash)) {
echo "Login Successful!";
} else {
echo "Invalid Credentials.";
}
}
Changing the Hashing Cost
You can control the cost factor (processing time). Higher = slower = more secure.
$options = ['cost' => 12];
$hashedPassword = password_hash($password, PASSWORD_BCRYPT, $options);
Default cost is 10. Higher values are more secure but can slow down login.
Rehashing Passwords
If the hashing algorithm or cost changes, use password_needs_rehash() to upgrade old hashes:
if (password_needs_rehash($storedHash, PASSWORD_DEFAULT)) {
$newHash = password_hash($password, PASSWORD_DEFAULT);
// Update the database
}
Password Hashing with Argon2 (PHP 7.2+)
Argon2 is currently one of the most secure algorithms.
$hash = password_hash($password, PASSWORD_ARGON2ID);
Memory-hard, secure against GPU cracking.
Securing Your PHP Password Handling Code
- Always Use HTTPS - Ensure password transmission is encrypted using SSL/TLS.
- Use Prepared Statements - Avoid SQL injection when handling login credentials.
$stmt = $pdo->prepare("SELECT password FROM users WHERE email = ?");
$stmt->execute([$email]);
- Limit Login Attempts - Implement brute-force protection using IP tracking or CAPTCHAs.
- Do Not Store Passwords in Sessions - Store only session identifiers or tokens.
Password Hashing vs Encryption
Feature | Hashing | Encryption |
---|---|---|
Reversible | No | Yes |
Use Case | Passwords | Data at rest |
Built-in PHP Support | password_hash() | openssl_encrypt() |