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. Since you are reading this guide, I will assume that you are looking for ways to create a more secure system. Yes, I understand that we are web developers and not security experts. But security is a big concern, it does not hurt to 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);

Yep, that’s all. But there are a few more ways to secure passwords in PHP – Let us walk through more examples, 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.

 

 

QUICK SLIDES

 

TABLE OF CONTENTS

Download & Notes Password Hash OpenSSL
Crypt MD5 & SHA1 Useful Bits & Links
The End

 

DOWNLOAD & NOTES

Firstly, here is the download link to the example code as promised.

 

QUICK NOTES

If you spot a bug, feel free to comment below. I try to answer short 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.

 

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.

 

 

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

 

1A) PASSWORD HASH

1-hash-verify.php
function addUser ($email, $password) {
  $sql = "INSERT INTO `users` (`email`, `password`) VALUES (?,?)";
  $hash = password_hash($password, PASSWORD_DEFAULT);
  $this->stmt = $this->pdo->prepare($sql);
  return $this->stmt->execute([$email, $hash]);
}
$pass = addUser("jon@doe.com", "password123");

To encrypt the password, simply use password_hash() to turn the clear text into an encrypted string.

  • By default, it will use the BCRYPT algorithm. You can change the algorithm, check out the full list here.
  • Make sure that you have allocated sufficient characters for the password field in the database. At the time of writing, the generated password hash is 60 characters. This may get longer with future algorithm updates.
  • Take note that password_hash() is a one-way encryption. Once encrypted, there is no way to easily decrypt.

 

1B) PASSWORD VERIFY

1-hash-verify.php
function verify ($email, $password) {
  $sql = "SELECT * FROM `users` WHERE `email`=?";
  $this->stmt = $this->pdo->prepare($sql);
  $this->stmt->execute([$email]);
  $user = $this->stmt->fetch();
  if (!is_array($user)) { return false; }
  return password_verify($password, $user["password"]);
}
$valid = verify("jon@doe.com", "password123");

This should be self-explanatory. To complete the password verification process, we simply use the sister password_verify() function.

 

 

METHOD 2) OPENSSL ENCRYPT & DECRYPT

This next method uses the openssl_encrypt() and openssl_decrypt() functions. Yes, this is 2-way encryption, where we can decrypt to get back the cleartext.

 

2A) OPENSSL ENCRYPTION

2-openssl.php
// KEEP IN A PROTECTED CONFIG FILE!
define ("SECRETKEY", "mysecretkey1234");

// ENCRYPT PASSWORD USING OPENSSL
function addUser ($email, $password) {
  $sql = "INSERT INTO `users` (`email`, `password`) VALUES (?,?)";
  $this->stmt = $this->pdo->prepare($sql);
  $hash = openssl_encrypt($password, "AES-128-ECB", SECRETKEY);
  return $this->stmt->execute([$email, $hash]);
}
$pass = addUser("joe@doe.com", "password456");
  • To use OpenSSL encryption, you need to define a secret key first. I.E. Your own “secret password”.
  • Don’t lose the secret key… You cannot decrypt without it.
  • There are many different cipher methods that we can use with openssl_encrypt(). You can get the full list available on your system using $methods = openssl_get_cipher_methods().
  • Again, make sure that you have allocated sufficient characters for the password field in the database.

 

2B) OPENSSL DECRYPT & VERIFY

2-openssl.php
function verify ($email, $password) {
  $sql = "SELECT * FROM `users` WHERE `email`=?";
  $this->stmt = $this->pdo->prepare($sql);
  $this->stmt->execute([$email]);
  $user = $this->stmt->fetch();
  if (!is_array($user)) { return false; }
  $plain = openssl_decrypt($user["password"], "AES-128-ECB", SECRETKEY);
  return $password==$plain;
}
$valid = verify("joe@doe.com", "password456");

There is no password verification function with OpenSSL. We decrypt the password from the database using openssl_decrypt(), and compare it against the user input.

 

 

2C) TWO-WAY ENCRYPTION – NOT A GOOD IDEA?

Yes, we can get the password back in cleartext with two-way encryption. Maybe this works well for some, for the purpose of automated password recovery. But think twice.

  • A compromised secret key will mean someone has access to all the passwords.
  • Lose the secret key and the system is as good as crippled.

So… Your choice.

P.S. This is still good in a way, for something like a “second password” – Every user keeps their own secret key, to encrypt-decrypt their own sensitive data on the server; We don’t store the users’ secret key, they lose it, the data is gone for good.

 

METHOD 3) CRYPT

Once upon a time in the iron age of PHP, password encryption is not as easy. We manually do our own encryption using crypt() and verification with hash_equals().

 

3A) CRYPT WITH SALT

3-crypt.php
function addUser ($email, $password) {
  $sql = "INSERT INTO `users` (`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([$email, $hash]);
}
$pass = addUserSalt("joy@doe.com", "password789");

To keep the long story short:

  • We can use crypt($password) without $salt. But this can be easily cracked with today’s processing power.
  • So in simple terms – A “salt” is used to further obfuscate the password, making it slightly harder to crack.
  • Yes, crypt() is a one-way hash. There is no such thing as a “decrypt function”.

 

3B) VERIFICATION

3-crypt.php
function verify ($email, $password) {
  $sql = "SELECT * FROM `users` WHERE `email`=?";
  $this->stmt = $this->pdo->prepare($sql);
  $this->stmt->execute([$email]);
  $user = $this->stmt->fetchAll();
  if (!is_array($user)) { return false; }
  return (hash_equals($user["password"], crypt($password, $user["password"])));
};
$valid = verify("joy@doe.com", "password789");

To verify, we do a hash_equals() check.

 

 

METHOD 4) MD5 & SHA1

If you don’t have a stomach for the above methods, then here is the final one – The classic, fast, lightweight MD5 and SHA1. Although these are fast algorithms, I don’t recommend using them, as they can be cracked with today’s technology with no sweat.

 

4A) MD5 & SHA1 ENCRYPT

4-md5-sha.php
function addUser ($email, $password) {
  $sql = "INSERT INTO `users` (`email`, `password`) VALUES (?,?)";
  $this->stmt = $this->pdo->prepare($sql);
  // CREDITS : https://www.thecodedeveloper.com/generate-random-alphanumeric-string-with-php/
  $salt = substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, 14);
  $password = $salt . md5($salt . $password);
  // $password = $salt . sha1($salt . $password);
  return $this->stmt->execute([$email, $password]);
}
$pass = addUser("job@doe.com", "password135");

Yes, MD5 and SHA1 are fast. But if you want to use it today, at least add some salt in.

 

4B) MD5 & SHA1 VERIFY

4-md5-sha.php
function verify ($email, $password) {
  $sql = "SELECT * FROM `users` WHERE `email`=?";
  $this->stmt = $this->pdo->prepare($sql);
  $this->stmt->execute([$email]);
  $user = $this->stmt->fetchAll();
  if (!is_array($user)) { return false; }
  $dbSalt = substr($user["password"], 0, 14);
  $dbPass = substr($user["password"], 14);
  return md5($dbSalt . $password) == $dbPass;
  // return sha1($dbSalt . $password) == $dbPass;
}
$valid = verify("job@doe.com", "password135");

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.

 

 

USEFUL BITS & LINKS

That’s all for this tutorial, and here are a few more extras and links that may be useful to you.

 

WHICH IS THE FASTEST?

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) 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>";

Which is the fastest of them all? The results on my dusty PC:

Conclusion, password_hash() and password_verify() is slow. But I will still recommend them for security. Does that split-second even matter? Only if you have a massive website with millions of users.

 

QUICK SUMMARY

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. Fast. Two-way encryption. Avoid using for passwords.
Crypt Low to medium. Fast. 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 Fast. Not recommended for passwords. Very easy to crack.

 

USE HTTPS!

No matter which encryption method you use, it is meaningless if you send cleartext passwords over the Internet. So make sure to set up proper HTTPS, it’s FREE these days anyway. The system is only as strong as its weakest link.

 

LINKS & REFERENCES

 

YOUTUBE TUTORIAL

 

INFOGRAPHIC CHEAT SHEET

Encrypt Decrypt in PHP (click to enlarge)

 

THE END

We have come to the end of this guide, I hope it has helped you with password encryption. The “super hackers” will probably laugh at the “simple encryption methods”. But why do people still use a lock, knowing that it can be broken into? Because it deters the not-so-savvy thieves. It is the same with cybersecurity, having a lock is better than nothing.

Thank you for reading. 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 *