The password_verify() function is an essential part of password security in PHP applications. This comprehensive technical guide will cover everything a developer needs to know about securely verifying passwords with password_verify().
What is the password_verify() Function?
The password_verify() function is used to verify if a provided plaintext password matches a hashed password stored in the database.
It takes two parameters:
-
$plaintextPassword – The plaintext password submitted by the user that needs verification
-
$hashedPassword – The hashed password from the database to verify against
password_verify() uses information embedded in the hash to determine the algorithm and options originally used to create it. It will then hash the plaintext password using the same algorithm and options and compare it to the hashed password from the database.
If the two match, it will return TRUE indicating the password is valid. If not, it will return FALSE indicating invalid credentials.
Why Use password_verify() for Verification?
Here are some key reasons why password_verify() should be used for password validation instead of manually checking passwords:
- Securely hashes and verifies passwords using strong one-way hashing algorithms like bcrypt
- Automatically handles password salting and key derivation
- Abstracts away the implementation complexity of cryptographic functions
- Easy to implement and integrate into existing apps or greenfield projects
- Built-in PHP function that is widely used and vetted for security issues
By encapsulating the complicated, low-level ASN.1 encoding and cryptography internals, password_verify() allows developers to easily incorporate industry password best practices into their PHP applications.
How to Hash Passwords with password_hash()
To utilize password_verify(), passwords need to be hashed and stored using the accompanying password_hash() function during registration:
$hashedPassword = password_hash($plaintextPassword, PASSWORD_DEFAULT);
This hashes the plaintext password provided using the recommended PASSWORD_DEFAULT algorithm, which currently uses the bcrypt hashing algorithm.
The hashed password contains embedded data about the algorithm and options used so later password_verify() can duplicate the same hash.
Password Hashing Algorithms
The PASSWORD_DEFAULT constant selects the strongest algorithm currently supported, currently bcrypt. But other options are available:
- PASSWORD_BCRYPT – Uses the CRYPT_BLOWFISH algorithm (bcrypt)
- PASSWORD_ARGON2I – Uses the Argon2i hashing algorithm
- PASSWORD_ARGON2ID – Uses the Argon2id hashing algorithm
Bcrypt Algorithm
The bcrypt algorithm is currently the most widely supported password hashing algorithm that password_verify() supports. Bcrypt incorporates a salt and performs multiple rounds of encryption to protect against rainbow table attacks.
Bcrypt remains resistant to brute force attacks even with increasing compute power due to its key derivation function. The cost factor can be increased over time to make the hash more computationally expensive as hardware improves.
Argon2 Algorithm
Argon2 is a newer algorithm standardized in 2015 that has greater resistance against side-channel attacks and tradeoff attacks. It has 3 versions:
- Argon2i – optimized against side-channel attacks
- Argon2d – optimized against GPU cracking attacks
- Argon2id – combines elements of both
Argon2id provides the best all-around protection arguably. However Argon2i is the most universally supported of the three at this time.
Bcrypt is more battle-tested and widely adopted, but Argon2 shows promise for the future with its improved design and security enhancements.
Hashing Options
Configurable hashing options can customize the cryptographic complexity when generating hashes:
- **cost** – Number of internal iterations made, higher is more secure but slower
- memory_cost – Memory allocated during hashing operations
- time_cost – Target hashing time in seconds
- threads – Number of parallel threads during hashing
These options allow tuning of how computationally expensive and time consuming the key derivation procedure is when creating password hashes.
You specify hashing options by passing an array as the 3rd argument:
$options = [‘cost‘ => 12];
$hashedPassword = password_hash($plaintextPassword, PASSWORD_BCRYPT, $options);
Increasing the cost makes hashes take longer to generate, hampering brute force attacks. Costs can be raised over time as computing power increases to counter stronger cracking capability.
password_verify() Usage Example
Here is a simple example validating a user‘s password submission using password_verify():
// Fetch hashed password for user from database
$hashedPasswordFromDB = ‘$2y$12$wiPvGNgNvX0F1x5rprWYyu5sClHU1EY22lC93WarXgaoFWoX9aj56‘;
// Plaintext password submitted by user
$plaintextPassword = ‘user-submitted-password‘;
// Verify submitted password against hashed password
if(password_verify($plaintextPassword, $hashedPasswordFromDB)) {
// Password match, login successful!
} else {
// Invalid password, deny access
}
This provides a straightforward blueprint for integrating password verification into a typical user login workflow.
When authenticating, fetch the hashed password for that user from your database or directory service, pass the plaintext password to password_verify() along with the hash, and check the Boolean response.
If TRUE, the password matches and login can proceed. If FALSE, the submitted password is incorrect and the login should fail.
Using Custom Salts
Salting passwords before hashing them protects against precomputed rainbow table attacks against common passwords.
password_hash() handles salting automatically by generating a cryptographically secure random salt each hash.
But you can override this and provide a custom salt as well, allowing migration of existing salted password systems:
$customSalt = ‘XadV62909!@*‘;
$hashedPassword = password_hash($plaintextPassword, PASSWORD_DEFAULT,
[‘salt‘ => $customSalt]);
Providing a salt this way will override the built-in randomly generated salt.
Salts should:
- Be unique per password hash
- Be securely randomly generated each hash
- Be long enough to provide sufficient entropy (128 bits+)
Password Encryption vs Hashing
Encryption and hashing represent two different cryptographic functions used for security:
- **Encryption** – Data transformed into ciphertext reversibly using a key. Two-way operation.
- **Hashing** – One-way transformation of data into fixed length value. Cannot be reversed.
While encrypting passwords may seem like a valid protection mechanism, properly hashing them is far more secure for authentication purposes.
Encryption has vulnerabilities when used with passwords:
- Requires keys to be stored which can be misused if accessed
- Mathematical properties allow cyphertext analysis attacks
- Encrypted values can be reversed to the original plaintext
Hashing avoids these issues by performing a destructive one-way conversion, removing the ability to ever recover the original plaintext password.
Statistics on Password Breaches
Implementing comprehensive password controls like password_verify() is vital based on statistics showing password insecurities as a common attack vector:
- 80% of hacking breaches leverage stolen or weak passwords
- 3.1 billion username and password combinations were exposed online in 2022
- 93% of passwords exposed online last year would take less than 1 second to crack
- 65% of people reuse the same password across multiple sites
These numbers demonstrate why properly hashing and verifying all authentication passwords according to industry best practices is essential.
OWASP provides up to date details in their password protection cheat sheet:
https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
OWASP Password Security Guidelines
The OWASP Application Security Verification Standard outlines current best practices for password-based authentication:
- Use randomly assigned salt values per password instance
- Utilize key derivation functions like bcrypt, scrypt or Argon2
- Hash passwords with a per user salted one-way cryptographic hash
- Compare credentials using constant time string comparison
- Store hashes securely using mechanisms to prevent disclosure
- Require secure transport like TLS for credential transmission
Additionally they recommend considering two-factor authentication, passwordless authentication, or limiting password attempts against online brute force attacks.
The password_verify() function aligns with the core hashing, salting, and comparison recommendations when used properly in context. But controls like secure transport layers and DoS attack prevention require additional server side controls.
Common PHP Password Security Mistakes
Despite password_verify() and password_hash() providing built-in protections, other common PHP password vulnerabilities exist:
Hardcoded Credentials
Exposing database credentials and other secrets in code risks disclosure. Use .env files or vaults instead for managing secrets.
Weak Random Salts
Insufficient random data when generating salts leads to predictable hashes vulnerable to rainbow tables. Use at least 128 bit salting values.
Not Using Prepared Statements
Concatenating unsanitized user input in queries allows SQL injection to obtain hashed passwords. Parameterize all SQL instead.
No 2FA/MFA
Two or multi-factor authentication adds extra identity confirmation beyond just usernames and passwords. SMS codes, biometrics, hardware tokens and mobile apps help harden authentication.
Unencrypted Password Storage
Never store plaintext passwords directly! Follow hashing best practices instead with cryptographic salts included.
Weak Access Controls
Principle of least privilege must restrict database and filesystem permissions, limiting exposure of hashed passwords.
OWASP provides additional recommendations like analyzing password rule strengths, testing credential stuffing protections, and security code reviews to uncover password flaws.
Alternative PHP Password Libraries
While password_hash() and password_verify() meet the needs of most implementations, alternatives exist:
phpass
One of the oldest and most widely used PHP password libraries is phpass. phpass focuses exclusively on secure password hashing and storage.
Pros:
- Lightweight library without external dependencies
- Supports blowfish, SHA-1, and MD5 hashing algorithms
- Easy migration from older password implementations
Cons:
- Only handles password functionality, unlike full PHPass library
- Some algorithms like MD5 considered deprecated
PHPass
PHPass is an open source portable password hashing framework supporting multiple PHP implementations.
Pros:
- Customizable hashing algorithms
- Handles password strength metering
- Extendable plugin framework
Cons:
- Requires specific PHP versions
- Complex documentation for first-time users
So while alternatives exist, password_verify() provides the best mixture of security and ease of use for most PHP password needs.
Example Code Snippets
Here are some common usage examples of password_verify() within authentication flows:
Local User Login
// Check submitted username and password on login form post
if(isset($_POST[‘submit‘])) {
$username = $_POST[‘username‘];
// Fetch hash for this user from database
$hashedPassword = $db->getUserHashedPassword($username);
if(password_verify($_POST[‘password‘], $hashedPassword)) {
// Login successful
} else {
// Invalid password
}
}
Admin Password Reset
// Set new password from admin using secure password reset token
public function resetPassword($resetToken, $newPassword) {
// Validate reset token
if($this->checkResetToken($resetToken)) {
// Hash new password
$hashedPassword = password_hash($newPassword, PASSWORD_DEFAULT);
// Update admin with new hashed password
$this->saveHashedPassword($hashedPassword);
}
}
User Registration
// Handle new user registration form post
public function registerNewUser($username, $password) {
// Generate new salt
$salt = random_bytes(16);
// Hash password with unique salt
$hashedPassword = password_hash($password, PASSWORD_DEFAULT, [‘salt‘ => $salt]);
// Save username with hashed password and salt
$this->storeUser($username, $hashedPassword, $salt);
}
Summary
The password_verify() function streamlines secure verification of user provided passwords against hashed versions stored in your PHP application databases. When combined with password_hash() for creating hashes, password_verify enables simple implementation of critical authentication security controls.
Usage avoids many common crypto pitfalls like unsalted hashes or weak irreversible encryption ciphers that lead to compromised credentials. password_verify leverages embedded hash metadata on algorithm and options to match hashes precisely.
While no singular control provides absolute protection, incorporatiing defense-in-depth controls like randomized salting, improved key derivation functions provided by password_verify, monitoring for suspicious activity, and supporting infrastructure like firewalls help minimize risks for PHP applications authenticating with passwords.
Adopting conventions promoted by industry authorities like OWASP and staying on top of latest advances in cryptographic security best practices helps ensure the highest levels of identity assurance and verification when using password-based systems.


