Password hashing is an important security concept when building user registration and login APIs. A password should never be stored directly in the database. Instead, PHP can create a secure password hash using password_hash().
Password hashing converts a plain-text password into a hash value that can be safely stored in a database.
Plain Password
↓
password_hash()
↓
Password Hash
↓
MySQL Database
The original password is not stored directly.
If a database is exposed, storing plain-text passwords can put users' accounts at serious risk.
| Hashing | Encryption |
|---|---|
| Designed to be one-way | Designed to be reversible with a key |
| Used for password storage | Used when data needs to be recovered |
| Verified against the original input | Decrypted back to readable data |
For passwords, PHP's password hashing functions are the appropriate approach.
PHP provides the password_hash() function for creating password hashes.
$password = "MyPassword123";
$hash = password_hash(
$password,
PASSWORD_DEFAULT
);
PASSWORD_DEFAULT tells PHP to use its recommended default password hashing algorithm.
$hash = password_hash(
$password,
PASSWORD_DEFAULT
);
Using PASSWORD_DEFAULT also allows PHP's recommended algorithm to evolve over time.
$password = "Hello123";
$hash = password_hash(
$password,
PASSWORD_DEFAULT
);
echo $hash;
The output is a password hash rather than the original password.
Calling password_hash() multiple times can produce different hashes for the same password because the hashing process includes a unique salt.
$password = "Hello123";
$hash1 = password_hash(
$password,
PASSWORD_DEFAULT
);
$hash2 = password_hash(
$password,
PASSWORD_DEFAULT
);
Both hashes can still be verified against the same original password.
A salt is additional random data incorporated into the password hashing process. PHP's password hashing functions manage the salt automatically.
$hash = password_hash(
"Hello123",
PASSWORD_DEFAULT
);
You do not need to manually create and store a separate salt when using password_hash().
During registration, store the generated hash in the password column.
$password = $data['password'];
$hashedPassword = password_hash(
$password,
PASSWORD_DEFAULT
);
$stmt = $pdo->prepare(
"INSERT INTO users
(name, email, password)
VALUES (?, ?, ?)"
);
$stmt->execute([
$name,
$email,
$hashedPassword
]);
Avoid storing passwords like this:
// Do NOT do this
$stmt->execute([
$name,
$email,
$password
]);
Instead, create a hash first.
$hashedPassword = password_hash(
$password,
PASSWORD_DEFAULT
);
During login, PHP can verify a plain password against the stored hash using password_verify().
if (password_verify(
$password,
$storedHash
)) {
echo "Password is correct";
}
Entered Password
↓
password_verify()
↓
Stored Password Hash
↓
Match?
┌────┴────┐
Yes No
↓ ↓
Login Reject
The application does not need to decrypt the stored hash.
$password = "Hello123";
$storedHash = $user['password'];
if (password_verify(
$password,
$storedHash
)) {
echo "Login successful";
} else {
echo "Invalid password";
}
During login, the API can find the user by email.
$stmt = $pdo->prepare(
"SELECT * FROM users
WHERE email = ?"
);
$stmt->execute([$email]);
$user = $stmt->fetch(
PDO::FETCH_ASSOC
);
if (!$user) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" => "Invalid email or password"
]);
exit;
}
It is common to use a general authentication error message rather than revealing whether an email address exists.
if (!password_verify(
$password,
$user['password']
)) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" => "Invalid email or password"
]);
exit;
}
if (password_verify(
$password,
$user['password']
)) {
echo json_encode([
"success" => true,
"message" => "Password verified"
]);
}
In a real authentication API, successful verification would normally be followed by an authentication step such as creating a token.
The database should contain the generated password hash.
| Column | Example Value |
|---|---|
| rahul@example.com | |
| password | Generated password hash |
The original password should not be stored in the password column.
The password column should have enough capacity to store the hash generated by PHP.
password VARCHAR(255)
A 255-character VARCHAR is commonly used for password hashes generated by PHP's password hashing functions.
The password should be validated before it is hashed.
$password = $data['password'] ?? '';
if ($password === '') {
sendError(
400,
"Password is required"
);
}
if (strlen($password) < 6) {
sendError(
422,
"Password must be at least 6 characters"
);
}
$hashedPassword = password_hash(
$password,
PASSWORD_DEFAULT
);
$stmt = $pdo->prepare(
"INSERT INTO users
(name, email, password)
VALUES (?, ?, ?)"
);
$stmt->execute([
$name,
$email,
$hashedPassword
]);
This is the basic password storage process.
$stmt = $pdo->prepare(
"SELECT * FROM users
WHERE email = ?"
);
$stmt->execute([$email]);
$user = $stmt->fetch(
PDO::FETCH_ASSOC
);
if (
!$user ||
!password_verify(
$password,
$user['password']
)
) {
http_response_code(401);
echo json_encode([
"success" => false,
"message" =>
"Invalid email or password"
]);
exit;
}
PHP also provides password_needs_rehash(). It can be used to determine whether an existing password hash should be updated to the current hashing configuration.
if (password_needs_rehash(
$user['password'],
PASSWORD_DEFAULT
)) {
// Generate a new hash
}
REGISTRATION
Plain Password
↓
password_hash()
↓
Hash
↓
MySQL
LOGIN
Entered Password
↓
password_verify()
↓
Stored Hash
↓
Match?
┌───┴───┐
Yes No
↓ ↓
Login Reject
<?php
$password = "Hello123";
$hash = password_hash(
$password,
PASSWORD_DEFAULT
);
echo "Hash: " . $hash;
if (password_verify(
$password,
$hash
)) {
echo "<br>Password is correct";
}
?>
This simple example demonstrates the complete hashing and verification process.
The React Native application sends the password securely to the API over HTTPS during registration or login.
fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
email: email,
password: password
})
});
The server is responsible for hashing the password during registration.
The password hash should not be returned in normal API responses.
// Avoid
echo json_encode([
"success" => true,
"user" => $user
]);
If $user contains the password field, the hash could be exposed. Select only the fields that the client actually needs.
echo json_encode([
"success" => true,
"user" => [
"id" => $user['id'],
"name" => $user['name'],
"email" => $user['email']
]
]);
<?php
$password = $data['password'] ?? '';
if ($password === '') {
sendError(
400,
"Password is required"
);
}
if (strlen($password) < 6) {
sendError(
422,
"Password must be at least 6 characters"
);
}
$hashedPassword = password_hash(
$password,
PASSWORD_DEFAULT
);
$stmt = $pdo->prepare(
"INSERT INTO users
(name, email, password)
VALUES (?, ?, ?)"
);
$stmt->execute([
$name,
$email,
$hashedPassword
]);
http_response_code(201);
echo json_encode([
"success" => true,
"message" => "Registration successful"
]);
?>
Password hashing protects user passwords when they are stored in a database. During registration, PHP uses password_hash() to create the hash. During login, PHP uses password_verify() to compare the entered password with the stored hash.
REGISTRATION
Password
↓
password_hash()
↓
Hash
↓
Database
LOGIN
Password
↓
password_verify()
↓
Stored Hash
↓
Authentication Result
Question: Which PHP function is used to verify a password against a stored password hash?