Effortless E-Commerce with PHP and MySQL: User Accounts
- Protecting Passwords
- Registration
- Logging In
- Logging Out
- Managing Passwords
- Improving the Security
The next step in the evolution of the “Knowledge Is Power” e-commerce site is to create a system of user accounts. When the site is complete, PayPal will be the crucial part in the registration process, but just to understand the user account system on its own, as well as to be able to create an administrative user for the next chapter, let’s look at user accounts as a separate entity first.
There are four primary facets to the implementation of user accounts in this chapter. First, a new user registers. Second, a registered user logs in. Third, the logged-in user logs out (in theory, many people, including me, don’t always do so). Fourth, users need to be able to retrieve a forgotten password and change an existing password.
Although this example won’t be storing any sensitive e-commerce data, security will still be taken seriously, for the benefit of the customers and the site itself. In a few places, I’ll make recommendations as to how you can increase security even further, and the chapter ends with even more suggestions. Chapter 12, “Extending the First Site,” makes even more suggestions—security-related and otherwise.
Protecting Passwords
How secure a user account system is will depend largely on how passwords are handled. Passwords can be stored on the server in three ways:
- In plain text, which is a terrible thing to do
- In an encrypted format, which can be decrypted
- In a hashed format, which can’t be decrypted
If you store passwords in an encrypted format, it’s safe from prying eyes and can be retrieved when necessary. But if someone gets onto your server and can find your code for performing the decryption, that person will be able to view every user’s password. And it turns out that you don’t need passwords to be decryptable: It doesn’t matter whether or not anyone can ever see the plain text in its original form again.
An alternative is to create a hash of the password. A hash is a representation of data. For example, MD5 is a hashing algorithm that’s been around for years. The MD5 hash of the word “password” is 5f4dcc3b5aa765d61d8327deb882cf99; the MD5 hash of the word “omnivore” is 04f7696e917f292f99925f80fcdb1db1. You can create a hash out of any piece of data, and, in theory, no two pieces of data have the same hash.
Storing the hash version of a password is more secure in that it can’t be decrypted. If hackers get your data, the best they can do is create hashes of common words in the hopes that they find the matching hash (this is called a dictionary attack). But storing a hash still makes logging in possible: When a user logs in, the hashed version of the user’s login password just needs to equal the already stored hashed version. If the two hashes equate, the submitted password is correct.
Once you’ve decided to hash the passwords, you’ll need to choose what hashing algorithm (or formula) to use and where the hashing should take place. By the latter I mean that you can hash the password in either the database or in your PHP code. Normally I recommend having the database do as much as possible, but PHP has more sophisticated hashing functions available than MySQL.
MD5 is a common legacy hashing algorithm, but not very secure. An improvement is SHA or SHA1, which are fine for some applications, although not e-commerce. For improved security, I’m going to turn to PHP’s very new password_hash() function. This function was added to the language as of PHP 5.5. This means you must have a current version of PHP in order to use it (as of this writing, the most current version is only 5.5.3).
If you aren’t running PHP 5.5 or greater, you can use an external library found at https://github.com/ircmaxell/password_compat. This library was created by Anthony Ferrara (http://blog.ircmaxell.com/) and is the basis for the version implemented in PHP 5.5. The library requires PHP 5.3.7 or greater.
To test whether you can use the library:
- Download the zip file from the GitHub URL just mentioned.
Unzip the downloaded file to create a folder of goodies (Figure 4.1).
- Copy the version-test.php file to your server’s web directory.
- Run the version-test.php in your browser (Figure 4.2).
Figure 4.2
If you’re not using PHP 5.5 or greater, and if the password_compat library indicates that it can’t be used, you should upgrade your PHP version. If that’s not possible either, post a message in my support forums (www.LarryUllman.com/forums/) for alternative hashing approaches.
To hash passwords with this new function, you’ll use this code:
$hash = password_hash($password, PASSWORD_BCRYPT);
That will work if you have PHP 5.5 or later. If you’re using the password_compat library, you must first include that library. Copy the lib directory from the downloaded file (see Figure 4.1) into your includes folder. Then include the library prior to invoking password_hash():
include('./includes/lib/password.php'); $hash = password_hash($password, PASSWORD_BCRYPT);
That’s all there is to it! It’s very simple and yet highly secure.
To verify a password upon login, use the password_verify() function. Its first argument is the submitted, unhashed password. The second is the stored, hashed password:
if (password_verify($password, $hash)) { /* Valid */ } else { /* Invalid */ }
Again, if you’re using the password_compat library you’ll need to include it prior to calling password_verify().