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($CLEAR, 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!

 

 

TABLE OF CONTENTS

 

DOWNLOAD & NOTES

Here is the download link to the example code, so you don’t have to copy-paste everything.

 

EXAMPLE CODE DOWNLOAD

Source code on GitHub Gist

Just click on “download zip” or do a git clone. I have released it under the MIT license, so feel free to build on top of it or use it in your own project.

 

SORRY FOR THE ADS...

But someone has to pay the bills, and sponsors are paying for it. I insist on not turning Code Boxx into a "paid scripts" business, and I don't "block people with Adblock". Every little bit of support helps.

Buy Me A Coffee Code Boxx eBooks

 

 

DUMMY USERS SYSTEM

Before we get into the password encryption/decryption methods, here is a quick baseline… Because not everyone is an expert and can see the entire picture immediately.

 

VIDEO TUTORIAL

 

DUMMY USERS DATABASE

0a-users.sql
CREATE TABLE `users` (
  `email` varchar(255) NOT NULL,
  `password` varchar(255) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
 
ALTER TABLE `users`
  ADD PRIMARY KEY (`email`);

For this example, we will be working with this database. Very simple. Only has 2 fields – email and password.

 

DUMMY USERS LIBRARY

0b-lib.php
class User {
  // (A) CONSTRUCTOR - CONNECT TO DATABASE
  private $pdo = null;
  private $stmt = null;
  public $error = null;
  function __construct () {
    $this->pdo = new PDO(
      "mysql:host=".DB_HOST.";dbname=".DB_NAME.";charset=".DB_CHARSET,
      DB_USER, DB_PASSWORD, [
      PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
      PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC
    ]);
  }
 
  // (B) DESTRUCTOR - CLOSE DATABASE CONNECTION
  function __destruct () {
    if ($this->stmt !== null) { $this->stmt = null; }
    if ($this->pdo !== null) { $this->pdo = null; }
  }
 
  // (C) HELPER - RUN QUERY
  function query ($sql, $data=null) : void {
    $this->stmt = $this->pdo->prepare($sql);
    $this->stmt->execute($data);
  }
 
  // (D) ADD/UPDATE USER
  function save ($email, $password) {
    $this->query("REPLACE INTO `users` (`email`, `password`) VALUES (?,?)", [$email, $password]);
    return true;
  }
 
  // (E) GET USER
  function get ($email) {
    $this->query("SELECT * FROM `users` WHERE `email`=?", [$email]);
    return $this->stmt->fetch();
  }
}

// (F) DATABASE SETTINGS - CHANGE TO YOUR OWN!
define("DB_HOST", "localhost");
define("DB_NAME", "test");
define("DB_CHARSET", "utf8");
define("DB_USER", "root");
define("DB_PASSWORD", "");
 
// (G) USER OBJECT
$_USR = new User();

Looks complicated, but is actually pretty straightforward.

  • (A, B, G) When $_USR = new User() is created, the constructor connects to the database. The destructor closes the connection.
  • (C) query() A helper function to run an SQL query.
  • (D) save() Save a user into the database.
  • (E) get() Get a user from the database.
  • (F) Change the database settings to your own.

 

 

PHP PASSWORD ENCRYPT & DECRYPT

All right, let us now get into the various ways to encrypt, decrypt, and verify passwords in PHP.

 

METHOD 1) PASSWORD HASH & VERIFY

1-hash-verify
<?php
// (A) USERS LIBRARY + USER + PASSWORD
require "0b-lib.php";
$email = "job@doe.com";
$password = "ABCD1234";

// (B) ENCRYPT PASSWORD
echo $_USR->save($email, password_hash($password, PASSWORD_DEFAULT))
  ? "OK" : "ERROR" ;

// (C) VERIFY PASSWORD
$user = $_USR->get($email);
echo password_verify($password, $user["password"])
  ? "VALID" : "INVALID" ;

As in the introduction, this is probably one of the easiest and fuss-free ways.

  • By default, password_hash() uses 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.

 

METHOD 2) OPENSSL

2-openssl.php
// (A) USERS LIBRARY + USER + PASSWORD + SECRET KEY
require "0b-lib.php";
$email = "joe@doe.com";
$password = "ABCD1234";
define ("SECRETKEY", "mysecretkey1234"); // keep in protected config file!
 
// (B) ENCRYPT PASSWORD
echo $_USR->save($email, openssl_encrypt($password, "AES-128-ECB", SECRETKEY))
  ? "OK" : "ERROR" ;

// (C) VERIFY PASSWORD
$user = $_USR->get($email);
echo openssl_decrypt($user["password"], "AES-128-ECB", SECRETKEY) == $password
  ? "VALID" : "INVALID" ;
  • To use OpenSSL encryption, you need to enable extension=openssl in php.ini.
  • You also need to define a secret key, your own “secret password” – define ("SECRETKEY", "SECRET").
  • Don’t lose the secret key. Keep it safe and never expose it publically.
  • 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().

P.S. OpenSSL encryption is two-way encryption. Yes, we can get the password back in cleartext. Maybe this works 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.

P.P.S. OpenSSL can still work 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, and the data is gone for good.

 

 

METHOD 3) CRYPT WITH SALT

3-crypt.php
// (A) USERS LIBRARY + USER + PASSWORD
require "0b-lib.php";
$email = "jon@doe.com";
$password = "ABCD1234";

// (B) ENCRYPT PASSWORD
// CREDITS : https://www.thecodedeveloper.com/generate-random-alphanumeric-string-with-php/
$salt = substr(base_convert(sha1(uniqid(mt_rand())), 16, 36), 0, 14);
echo $_USR->save($email, crypt($password, $salt))
  ? "OK" : "ERROR" ;

// (C) VERIFY PASSWORD
$user = $_USR->get($email);
echo hash_equals($user["password"], crypt($password, $user["password"]))
  ? "VALID" : "INVALID" ;

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(). 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”.

 

METHOD 4) MD5 & SHA1

4-md5-sha.php
// (A) DATABASE LIBRARY + USER + PASSWORD
require "0b-database.php";
$email = "joy@doe.com";
$password = "ABCD1234";
 
// (B) ENCRYPT PASSWORD
// 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);
// $hash = $salt . sha1($salt . $password);
echo $_USR->save($email, $hash)
  ? "OK" : "ERROR" ;
 
// (C) VERIFY PASSWORD
$user = $_USR->get($email);
$dbsalt = substr($user["password"], 0, 14);
$dbpass = substr($user["password"], 14);
// echo sha1($dbsalt . $password) == $dbpass
echo md5($dbsalt . $password) == $dbpass
  ? "VALID" : "INVALID" ;

Finally, this is yet another classic password encryption method we use in the Stone Age – By using md5 or sha1. Take note, these are Stone Age methods. With today’s processing power, md5 and sha1 can be easily cracked. So if you want to use it today, at least add some salt in.

 

 

EXTRAS

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 METHOD?

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

 

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 *