When it comes to password security you can never be too paranoid.

In this article, I am going to teach you how to securely save your users’ passwords in an MYSQL database and retrieving them without the fear of being vulnerable to timing attacks.

You should never under any circumstances save the plain password in database!

Why? I feel like I don’t even have to ask this question.

But, for the sake of it let’s do it.

Suppose you have a website with 3000 customers or users, and your database gets hacked.

Well, guess what, Jack?

All your sensitive data, is not so sensitive now, is it?

The attacker now has the login credentials for all of your 3000 users. To add more to this, imagine if your website it’s an eCommerce shop.

Total disaster!

Ok, I’m busted! How to prevent this?

The secure way to store a password into database with PHP

Please note that you will need at least PHP >= 5.6.0 installed on your web server for this method to work, but it would be highly recommended and encouraged that you have PHP 7.2+ version.

Why am I saying this?

Let’s have a look at the official PHP supported versions.

According to PHP, version 7.1 will end it’s security support on December 1st, 2019, therefore 7.2+ would be a must.

So if you don’t already have this version, contact your hosting administrator with this request. You could also do it if you have such knowledge.

The main functions that we are going to use to achieve top-notch security are password_hash() and password_verify(), which are totally safe against timing attacks. But first, let’s learn the basics of it.

What the heck are timing attacks?

Let’s start with the official answer from Wikipedia:

In cryptography, a timing attack is a side-channel attack in which the attacker attempts to compromise a cryptosystem by analyzing the time taken to execute cryptographic algorithms.

Every logical operation in a computer takes time to execute, and the time can differ based on the input; with precise measurements of the time for each operation, an attacker can work backward to the input

And with an explanation from StackExchange:

The standard string comparison algorithm stops as soon as it finds a non-matching byte.

This leaks information about the string contents through time differences, because the longer the common prefix, the longer the comparison takes.

Still confused? Here are 2 scenarios:

Let’s make 2 PHP string checks:

<?php 

/* 
/ * 1st Example - Compare 'aaaX' with 'aaaa'
*/

if ( 'aaaX' == 'aaaa' ){
   echo 'Found';
}
else{
   echo 'NOT Found';
}

/* 
/ * 2nd Example - Compare 'Xaaa' with 'aaaa'
*/

if ( 'Xaaa' == 'aaaa' ){
   echo 'Found';
}
else{
   echo 'NOT Found';
}

?>
  • In the first example, PHP compare 3 bytes and then return the result as the 4th byte did not match (“X did not match with a“)
  • In the second one, PHP returns after the first byte (“X did not match with a“)

At least, in theory, there are time differences in PHP execution between 1st example and the second one, and this is how attackers could “guess” your password.

In a real attacking scenario, let me quote another explanation from StackExchange, as I could not explain any better:

In an attack scenario, an attacker has total control of $mac1 (it’s taken from the attacker-made message), while $mac2 in the real valid MAC for the attacker’s message. $mac2 must remain secret from the attacker, or he can stick it on his message and thus forge a valid message.

The attacker, by analyzing the time it takes to get a response, can probably figure out where the first difference is between his MAC and the real one.

He can try all possibilities for that one byte, find the correct one, and then work on the next byte secure in the knowledge that the first k bytes are right.

In the end, he tried just 256 x 256 MACs (if 256 is the length of the MAC) instead of the 256256 he should have had to try.

Timing attacks can also occur in any other programming languages such as Python, C, Java, Ruby etc.

Goodbye timing attacks!

Thanks to the new functions introduced in PHP 5.6.0 and above, you can stop worrying about timing attacks, as this functions were created to safely compare values. First, the entire string is compared, and only then the result is returned.

Which is the safest way to save a password in database?

The safest way it’s that you never save the password itself.

Instead, you use password_hash() function, and save a hash of that password.
Don’t be scared, a password hash it’s nothing more than an encoded string.

The hash generated by the password_hash() has a strong one-way hashing algorithm, which means that you can’t revert it and find out the user’s password.

You only compare the hash with the actual user-submitted password, and if there is a match it means the password it’s correct.

Consequently, using the hash method, you will never store the real password, hence you will have no problems in case your database gets hijacked.

When you make the check you will use password_verify() and compare the user input password with your database hash

Here’s an example:

<?php
/*
* this is the safe password hash that can be inserted in database
* it will look something like this : 
* $2y$10$X/ssngeVsmNvXpyCDc8I/uPG8Luiy5Z6LebcUOYXog5mD8GC2OSCS
*/
$safe_password_hash = password_hash( "user_password", PASSWORD_DEFAULT );

/*
* password_hash() accepts 3 params
*
* @param1 - password - which is the regular password
* @param2 - algo - encryption algorithm - default PASSWORD_DEFAULT 
* @param3 - options - an array of options 
*
* see more details on : 
* https://www.php.net/manual/en/function.password-verify.php
*/

// make your insert
$sql = "INSERT INTO user_table ( password ) VALUES ( '$safe_password_hash' )";
$conn->query( $sql );

/*
* when user is logging in make the check with password_verify()
* $GET['password'] -  it's the user submitted password
* $safe_password_hash -  it's your password hash value from database
*/
$check =  password_verify ( $GET['password'] , $safe_password_hash );

if ( $check ){
   //user password and hash matched - Safe to login or do other stuff
}
else{
   //NO match - incorrect password
}

?>

And there you have it!

This is the right way to do it, and if you haven’t done it already, consider twice!

Wrap it up John! I got things to do!

In conclusion, it is never a good idea to cut corners when it comes to sensitive information, because in a real life situation, you could face yourself with some serious headaches if something like this occurs.

As a final note, take that old saying into account: Better safe than sorry!

If this article helped you, please spread the word among humans, so others can learn too ! 🙂
Feel free to add a comment or suggestion.