4 Simple Ways to Encrypt Decrypt Verify Passwords in PHP

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 PHP Password Hash OpenSSL Encrypt Decrypt
Crypt MD5 & SHA1 Useful Bits & Links
The End

 

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:

1-hash-verify.php
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:

1-hash-verify.php
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

2-openssl.php
// 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().

2-openssl.php
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.

2-openssl.php
// 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

3-crypt.php
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

3-crypt.php
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”.

3-crypt.php
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.

4-md5-sha.php
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.

4-md5-sha.php
// 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:

4-md5-sha.php
// 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:

5-test.php
<?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

Encrypt Decrypt in PHP (click to enlarge)

 

LINKS & REFERENCES

 

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!

4 thoughts on “4 Simple Ways to Encrypt Decrypt Verify Passwords in PHP”

  1. 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.

    1. 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.

  2. 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.

Leave a Comment

Your email address will not be published. Required fields are marked *