Welcome to a tutorial on the various ways to encrypt, decrypt, and verify passwords in PHP. If you are reading this guide, I am going to assume that you are not a security expert and looking for ways to create a more secure system. Yes, I totally understand that we are web developers and not security experts.
But in the cyber world where security is a big concern, there is one thing that I have learned very well over the donkey years within the industry – At least know how to put a lock on your systems.
An easy way to protect passwords in PHP is to use the password hash and verify functions.
$hash = password_hash($password, PASSWORD_DEFAULT);
$verified = password_verify($CLEAR, $hash);
But there are a few more ways to secure passwords in PHP – Let us walk through more examples in this guide, minus all that complicated Math stuff. Read on!
ⓘ I have included a zip file with all the code examples at the start of this tutorial, so you don’t have to copy-paste everything… Or if you just want to dive straight in.
TABLE OF CONTENTS
DOWNLOAD & NOTES
Firstly, here is the download link to the example code as promised.
EXAMPLE CODE DOWNLOAD
Click here to download all the examples, I have released it under the MIT license, so feel free to build on top of it or use it in your own project.
QUICK NOTES
If you spot a bug, please feel free to comment below. I try to answer questions too, but it is one person versus the entire world… If you need answers urgently, please check out my list of websites to get help with programming.
A) PHP PASSWORD HASH
When it comes to password encryption, there is always a big confusing algorithm behind it. Thankfully, PHP has a fuss-free password hash and password verify function. The usage is very straightforward, and they work in a pair.
A1) ENCRYPTION
To encrypt the password, you simply use the password_hash()
function in your library function before saving the user. For example:
function addUser($name, $email, $password){
$sql = "INSERT INTO `users` (`name`, `email`, `password`) VALUES (?,?,?)";
$this->stmt = $this->pdo->prepare($sql);
$hash = password_hash($password, PASSWORD_DEFAULT);
return $this->stmt->execute([$name, $email, $hash]);
}
$pass = addUser("John Doe", "john@doe.com", "password123");
- By default, the encryption method used will be the
BCRYPT
algorithm. You can change it to use Blowfish (PASSWORD_BCRYPT
) or Argon (PASSWORD_ARGON2I
). - Make sure that you have allocated sufficient characters for the password field in the database. The generated password hash is 60 characters, and it may get longer with algorithm updates.
- Please take note that
password_hash()
is a one-way encryption. There is no way you can decrypt that easily, so you will have to ask the user for a new password for password recovery.
A2) VERIFICATION
To complete the password verification process, we simply use the sister password_verify()
function in the login check:
function login($email, $password){
$sql = "SELECT * FROM `users` WHERE `email`=?";
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute([$email]);
$user = $this->stmt->fetchAll();
return password_verify($password, $user['password']);
}
$valid_user = login($_POST['email'], $_POST['password']);
B) OPENSSL ENCRYPT AND DECRYPT
This next method uses the OpenSSL encrypt and decrypt functions, which I think are much more flexible since they are 2-way encryptions. You can use these to protect not just the passwords, but also use it to encrypt-decrypt sensitive data.
B1) ENCRYPTION
// Keep your secret key somewhere safe
// In config file, in a secured folder not publically accessible
define ("SECRETKEY", "mysecretkey1234");
// Encrypt the password using the openssl_encrypt function & your secret key
function addUser($name, $email, $password){
$sql = "INSERT INTO `users` (`name`, `email`, `password`) VALUES (?,?,?)";
$this->stmt = $this->pdo->prepare($sql);
$hash = openssl_encrypt($password, "AES-128-ECB", SECRETKEY);
return $this->stmt->execute([$name, $email, $hash]);
}
$pass = addUser("Jane Doe", "jane@doe.com", "password456");
- To use the OpenSSL encryption, you need to define your secret key first, I.E. Your own “secret password”.
- Just don’t lose the secret key… or hell will freeze over and you cannot decrypt the data without it.
- There are many different cipher methods that you can use with
openssl_encrypt()
, and you can get a list of it using the get cipher function. - Again, make sure that you have allocated sufficient characters for the password field in the database.
B2) VERIFICATION
There is no password verification function with OpenSSL, but we will be decrypting the password from the database using openssl_decrypt()
.
function login($email, $password){
$sql = "SELECT * FROM `users` WHERE `email`=?";
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute([$email]);
$user = $this->stmt->fetchAll();
$plain = openssl_decrypt($user['password'], "AES-128-ECB", SECRETKEY);
return $password==$plain;
}
$valid_user = login($_POST['email'], $_POST['password']);
B3) PROTECTING SENSITIVE DATA
Since OpenSSL is a 2-way encrypt-decrypt, you can also use it to protect other sensitive information such as address and contact numbers.
// Turn address into JSON string & encrypt
$addresses = [
"Test Street 1234 Somewhere 2345",
"Doge Street 1234 Cate Country 2345"
];
$cipher = openssl_encrypt(json_encode($addresses), "AES-128-ECB", SECRETKEY);
$sql = "INSERT INTO `addresses` (`user_id`, `data`) VALUES (?,?)";
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute([$id, $cipher]);
// Decrypt & JSON decode
$sql = "SELECT * FROM `address` WHERE `id`=?";
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute([$id]);
$address = $this->stmt->fetchAll();
$address = json_decode(openssl_decrypt($address, "AES-128-ECB", SECRETKEY));
B4) TWO-WAY ENCRYPTION – GOOD PRACTICE?
Yes, with two-way encryption, we can get the password back in cleartext. Maybe it works conveniently for some people who like to do it for password recovery. But think twice – A compromised secret key will mean the bad code ninjas will have access to all the rest of the passwords in the database. So… Your choice.
C) CRYPT
Once upon a time in the iron age of PHP, password encryption is not as easy and friendly. Instead of using the password hash function, we have the ancestor called crypt and hash equals.
C1) CRYPT
function addUser($name, $email, $password){
$sql = "INSERT INTO `users` (`name`, `email`, `password`) VALUES (?,?,?)";
$this->stmt = $this->pdo->prepare($sql);
$hash = crypt($password);
return $this->stmt->execute([$name, $email, $hash]);
}
$pass = addUser("Joy Doe", "joy@doe.com", "password789");
Nope, there is no decrypt function as this is a one-way algorithm.
C2) VERIFICATION
function login($email, $password){
$sql = "SELECT * FROM `users` WHERE `email`=?";
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute([$email]);
$user = $this->stmt->fetchAll();
return (hash_equals($user['password'], crypt($password, $user['password'])));
};
$valid_user = login($_POST['email'], $_POST['password']);
C3) BEEFING UP CRYPT WITH SALT
For you guys who are on PHP 5.6 and above, you will be getting the “not secure enough” notice if you just crypt with providing a salt. Yes, the crypt function does take a second parameter, a salt to further obfuscate the password. There are probably endless ways to generate a good salt, but for the sake of simplicity, I will just demonstrate with a randomly generated “long enough string”.
function addUserSalt($name, $email, $password){
$sql = "INSERT INTO `users` (`name`, `email`, `password`) VALUES (?,?,?)";
$this->stmt = $this->pdo->prepare($sql);
// GENERATE RANDOM STRING (SALT)
// CREDITS : https://www.thecodedeveloper.com/generate-random-alphanumeric-string-with-php/
$salt = substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, 14);
$hash = crypt($password, $salt);
return $this->stmt->execute([$name, $email, $hash]);
}
$pass = addUserSalt("Joy Doe", "joy@doe.com", "password789");
D) MD5 & SHA1
If you don’t have a stomach for the all of the above methods, then here is the final one – The classic, fast, lightweight MD5 and SHA1. Although they are fast algorithms, I don’t recommend using them, as they can be cracked pretty easily with today’s technology.
D1) ENCRYPT
Yes, MD5 and SHA1 are also 1-way algorithms. Don’t use them to encrypt sensitive data… Even though they can be decrypted, that will still take quite some time.
function addUser($name, $email, $password){
$hash = md5(password);
// $hash = sha1(password);
$sql = "INSERT INTO `users` (`name`, `email`, `password`) VALUES (?,?,?)";
$this->stmt = $this->pdo->prepare($sql);
return $this->stmt->execute([$name, $email, $hash]);
}
$pass = addUser("Jordan Doe", "jordan@doe.com", "password135");
D2) VERIFY
Verification with MD5 and SHA1 is just as easy. Just rehash the password that the user has input, and check it against the one in the database… I guess you don’t even need to be a security expert to see why I don’t recommend using these 2 to protect the passwords.
// COMPARE THE HASH IN VERIFICATION
function login($email, $password){
$sql = "SELECT * FROM `users` WHERE `email`=?";
$this->stmt = $this->pdo->prepare($sql);
$this->stmt->execute([$email]);
$user = $this->stmt->fetchAll();
return $user['password'] == md5($password);
// return $user['password'] == sha1($password);
}
$valid_user = login($_POST['email'], $_POST['password']);
D3) BEEF IT UP
Raw MD5 and SHA1 can be easily cracked with today’s technology. If you want to use it, I will recommend at least add some salt to the password to further obfuscate it:
// Credits : https://www.thecodedeveloper.com/generate-random-alphanumeric-string-with-php/
$salt = substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, 14);
$hash = $salt . md5($salt . $password);
// Decrypt
// Extract salt from hash then verify
$dbSalt = substr($user['password'],0,14);
$dbPass = substr($user['password'],14);
if (md5($dbSalt . $password) == $dbPass) { /* CORRECT PASSWORD */ }
USEFUL BITS & LINKS
All right, we have now covered all 4 different ways to deal with passwords in PHP. This section is a little bit of an extra, of things that you need to take note of, and the download link to all the examples above.
PERFORMANCE TEST – WHICH IS THE BEST?
So which is the best method? Here is a little test script:
<?php
// (A) PHP PASSWORD HASH
$start = microtime(true);
$clear = "MyPassw@rd!23";
$hash = password_hash($clear, PASSWORD_DEFAULT);
$endA = microtime(true);
$verified = password_verify("MyPassw@rd!23", $hash);
$endB = microtime(true);
$tenc = $endA - $start;
$tdec = $endB - $endA;
$ted = $tenc + $tdec;
echo "PHP password_hash() + password_verify()<br>";
echo "Time taken to encode = " . $tenc . " sec <br>";
echo "Time taken to verify = " . $tdec . " sec <br>";
echo "Total time taken = " . $ted . " sec <br><br>";
// (B) OPENSSL (AES-128-ECB)
$start = microtime(true);
$clear = "MyPassw@rd!23";
$hash = openssl_encrypt($clear, "AES-128-ECB", "mysecretkey1234");
$endA = microtime(true);
$decrypt = openssl_decrypt($hash, "AES-128-ECB", "mysecretkey1234");
$verified = $decrypt == $clear;
$endB = microtime(true);
$tenc = $endA - $start;
$tdec = $endB - $endA;
$ted = $tenc + $tdec;
echo "OpenSSL 128-bit AES<br>";
echo "Time taken to encode = " . $tenc . " sec <br>";
echo "Time taken to verify = " . $tdec . " sec <br>";
echo "Total time taken = " . $ted . " sec <br><br>";
// (C) CRYPT
$start = microtime(true);
$clear = "MyPassw@rd!23";
$salt = substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, 14);
$hash = crypt($clear, $salt);
$endA = microtime(true);
$verified = hash_equals($hash, crypt($clear, $hash));
$endB = microtime(true);
$tenc = $endA - $start;
$tdec = $endB - $endA;
$ted = $tenc + $tdec;
echo "Salted Crypt + Hash Equals<br>";
echo "Time taken to encode = " . $tenc . " sec <br>";
echo "Time taken to verify = " . $tdec . " sec <br>";
echo "Total time taken = " . $ted . " sec <br><br>";
// (D) MD5
$start = microtime(true);
$clear = "MyPassw@rd!23";
$hash = md5($clear);
$endA = microtime(true);
$verified = $hash == md5($clear);
$endB = microtime(true);
echo "MD5<br>";
echo "Time taken to encode = " . $tenc . " sec <br>";
echo "Time taken to verify = " . $tdec . " sec <br>";
echo "Total time taken = " . $ted . " sec <br><br>";
// (E) SALTY MD5
$start = microtime(true);
$clear = "MyPassw@rd!23";
$salt = substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, 14);
$hash = $salt . md5($salt . $clear);
$endA = microtime(true);
$dbSalt = substr($hash,0,14);
$dbPass = substr($hash,14);
$verified = md5($dbSalt . $clear) == $dbPass;
$endB = microtime(true);
echo "MD5 Salted<br>";
echo "Time taken to encode = " . $tenc . " sec <br>";
echo "Time taken to verify = " . $tdec . " sec <br>";
echo "Total time taken = " . $ted . " sec <br><br>";
On my dusty desktop, here are the results:
Conclusion, openssl_encrypt()
128-bit AES is blazing fast and password_hash()
using BCRYPT is freaking slow. But speed is not everything, there are other considerations.
Method | Security Level | Performance | Notes |
Password hash and verify | Medium to high. Depending on the algorithm. | Slow. | One way. Good for protecting passwords, a bit slow though. |
OpenSSL encrypt | Low to high. Depending on the algorithm. | Blazing fast. | Two-way encryption. Can be used for passwords and sensitive data. Not really recommended though, a compromised secret key equals to the entire system compromised. |
Crypt | Low to medium. | Good. | One way, standard DES. This one is not really secure “out-of-the-box”. Add your own salt to beef it up. |
MD5 & SHA1 | Very Low | Excellent. | Not recommended for passwords. Proven to be very easy to crack. |
Personally, I will go with password hash and verify, slightly slower but secure. Then for sensitive data, use OpenSSL. Feel free to disagree and share your thoughts though… I am a code ninja, not some super hacker security guru.
USE HTTPS!
Remember that no matter which encryption method you use, it is rather meaningless if you send cleartext passwords over the Internet using plain HTTP. So please make sure that you use the proper HTTPS, and install a server-side firewall to plug the security loopholes – The entire system is only as strong as the weakest link.
THE COST OF ENCRYPTION
Of course, I will 100% recommend encrypting the passwords. But you have to remember that all the encode-decode are algorithms that use system resources – The more complex they are, the more calculations will be made, the more system resources they use. Do your password encryptions, keep sensitive data safe by encrypting them, but don’t go overkill and encrypt every Tom, Dick, and Harry.
YOUTUBE TUTORIAL
THE CHEAT SHEET

LINKS & REFERENCES
- Password hash on PHP
- Password verify on PHP
- OpenSSL Encrypt
- OpenSSL Decrypt
- PHP Crypt
- PHP Hash Equals
- MD5, SHA1
THE END – ANY LOCK IS BETTER THAN NONE
Thank you for reading, and we have come to the end of this guide. I hope it has given you some insight on password encryption, but please take note that I am not a security expert as well, and a few of the “super hackers” will probably laugh at the “simple encryption methods”.
But I shall still share a piece of advice – Why do people use a lock, even knowing that it can be broken into? Because that will deter most of the not-so-savvy thieves. It is the same with cybersecurity, and having a lock is better than nothing; It does not hurt to do minimal password encryption.
If you have anything to add to this guide, please feel free to comment below. Good luck and happy coding!
While I admire the effort you put into writing the article, I think you should have done some more research on best practices when dealing with passwords. Passwords should _never_ be stored in a reverse-able format. It’s worse then using older and outdated hashing algorithms like MD5.
While I admire the effort you put into writing this comment, I think you should have put more effort into reading the entire tutorial. 🙄
* I don’t recommend using MD5 and SHA1, as they can be cracked pretty easily with today’s technology.
* A compromised secret key in 2-way encryption = entire system compromised.
But thanks anyway, maybe the points were not emphasized nor made clear enough.
Brilliant tutorial thanks. It would be nice to know which system is the best one in your experience.
Hi thank you so much for this tutorial really helps me to develop must secure login to my system. I am a beginner programmer hope I will be better someday.