Secure Passwords with Phpass

When we create an account on a website, we are essentially trusting the service to be secure and we pray that our data will be safe. But, is it? Implementing good security in a website can sometimes seem like a daunting task, but the trick is to attack each vulnerability one by one. Passwords are one of those vulnerabilities, and users expect that your system will securely encrypt them so that prying eyes can stay shut.

A while back ago I decided that I wanted to learn more about proper password encryption, and so I came across Phpass: a framework aimed at simplifying the process for encrypting or “hashing” passwords. Phpass has been integrated into many well-known CMS’s such as WordPress, Vanilla, phpBB, and Drupal, and is a huge reason for why I trust this framework. The only downside was that I could not find an easy-to-follow tutorial on using it, so I decided to write my own.

Current Insecure Techniques

Before we jump into Phpass, let’s take a look at some of the wrong techniques of storing passwords. First would be pretty obvious: storing the raw password. Not only does this open up your website to hackers, but legal authorities will probably come after you as well. The next and probably most popular way that developers secure passwords is by the use of the md5() function. It’s better than storing raw passwords, but is not well suited for password encryption as it can easily be broken.

The code below should never be used to hash a user’s password.

[code lang="php"]

$hash = md5("the password");

?>
[/code]

I will note that the sha1() and hash() functions are slightly more secure (especially in combination) but still don’t give as much protection against hackers as Phpass.

Using Phpass

It’s actually very easy to start using Phpass, so go ahead and download the framework at Openwall. Extract and open the folder until you see the file PasswordHash.php. This is the only file that you will be needing so upload it to wherever your website files are stored.

You must then include the file into wherever you will be working with passwords.

[code lang="php"]

require("PasswordHash.php");

?>
[/code]

Make sure that the path to PasswordHash.php is correct. If you receive no errors, then Phpass is ready to be used.

Next you will want to construct the class with the proper arguments. The default arguments specified should be fine for up-to-date installations of PHP. The first argument specifies the “base-2 logarithm of the iteration count used for password stretching” and the second argument specifies the use of portable hashes.

[code lang="php"]

$hasher = new PasswordHash(8, false);

?>
[/code]

Creating the Hash—Signup

For something like a signup system, you will now want to create a hash of the password. In a typical situation, the password will be coming from a form that the user filled out.

[code lang="php"]

// In this case, the password is retrieved from a form input
$password = $_POST["password"];

// Passwords should never be longer than 72 characters to prevent DoS attacks
if (strlen($password) > 72) { die("Password must be 72 characters or less"); }

// The $hash variable will contain the hash of the password
$hash = $hasher->HashPassword($password);

if (strlen($hash) >= 20) {

// Store the hash somewhere such as a database
// The code for that is up to you as this tutorial only focuses on hashing passwords

} else {

// something went wrong

}

?>
[/code]

The $hash variable now contains the hash that you will need to store somewhere such as a database. Make sure to never actually store the raw password, only the hash that Phpass generates.

Also take a note the following:

  • The password limit should be set to 72 characters to prevent certain DoS attacks. We do this here with the strlen() function.
  • The hash can never be less than 20 characters, so if it is then something went wrong during the encryption process. A simple if statement is done for this as well.

Checking “Matched” Passwords—Login

For something like a login system, you will want to be able to check that the provided password matches up against your stored hash. Include the framework in if you haven’t already.

[code lang="php"]

require("PasswordHash.php");

$hasher = new PasswordHash(8, false);

?>
[/code]

There is no way to actually “decode” the hash (which is the point), instead we let Phpass do its magic for finding out if the password matches up against the stored hash.

[code lang="php"]

// Password from form input
$password = $_POST["password"];

// Passwords should never be longer than 72 characters to prevent DoS attacks
if (strlen($password) > 72) { die("Password must be 72 characters or less"); }

// Just in case the hash isn't found
$stored_hash = "*";

// Retrieve the hash that you stored earlier
$stored_hash = "this is the hash we stored earlier";

// Check that the password is correct, returns a boolean
$check = $hasher->CheckPassword($password, $stored_hash);

if ($check) {

// passwords matched! show account dashboard or something

} else {

// passwords didn't match, show an error

}

?>
[/code]

The $check variable will return true if the passwords matched. It’s a good idea to then either display some sort of account dashboard for the user, or redirect them to their profile. If the check failed, you will need to send them back to the login form or output some sort of error message.

Extra Resources

That is all it takes to start using Phpass, so pat yourself on the back for being a security-aware developer. Of course you should also make sure that your website is safe against other vulnerabilities such as SQL Injection, which can also be easily fixed by using prepared statements or the database framework I wrote earlier.

If you need a bit more help on getting started with Phpass, I recommend you check out this starter project I made on GitHub. It basically follows all the steps in this post but is separated into files as well as including the Phpass framework. Also feel free to read the more lengthy article about secure passwords on Openwall.

  • mats

    Thanks a lot, been trying to get this working for 2 hours.. -.-

    But thanks to your guide I now got it running!

    • http://sunnyis.me/ Sunny Singh

      Glad it helped :)

  • Guicara

    It’s more secure that use a SHA-1 (or MD5) password with a salt?
    The salt is stored in the database (in cleartext) with password’s hash.

    • http://sunnyis.me/ Sunny Singh

      MD5 is one of the most insecure methods of hashing a password. I don’t know much about salts, so I can’t comment on whether using it with a salt will be secure. For me, dropping in a library like phpass makes more sense than building my own password hashing system that will be arguably less secure.

  • Paulo

    Excellent Article!
    I don’t get one thing. For example, when I’m going to register a new User on the database, I store the hashed password, right?But as far as I see, I would have to loop through the whole database, checking if any password matches, one by one, using the CheckPassword function.
    I can’t use something like: SELECT id FROM users WHERE password = ‘hashed_pass’, because even the same password got a different hash with the salt thing.

    Is it really this way? Or am I missing something?
    Thank you :)

    • http://sunnyis.me/ Sunny Singh

      As far as I know, the CheckPassword function is the only way to check a plain text password against a stored hash. There is usually no need to do any looping because you can simply select the user’s data via their email or username, and then check the stored hash with the password they provided.

      So instead of looping for password matches, just retrieve the stored password hash by using their email or username (depends on your user registration system).

  • http://larrybolt.me/ Larry Boltovskoi

    I don’t get why there is a limit of 72 chars on a password. And you use a nice method to hash the passwords, but I believe for some applications if would be better to return the hash so a regular lookup can be fired at the database “SELECT * FROM users WHERE username=#username# AND password=#hash#”

    But that’s something I can’t really argue about.

    • http://sunnyis.me/ Sunny Singh

      From the Phpass site:

      /* Don't let them spend more of our CPU time than we were willing to.
      * Besides, bcrypt happens to use the first 72 characters only anyway. */
      if (strlen($pass) > 72)
      fail('The supplied password is too long');

      So basically it’s more secure and sane to provide a max length limit on the password.

      • http://larrybolt.me/ Larry Boltovskoi

        Thanks! I didn’t knew that.

    • Guest

       It doesn’t make sense to do it like that. As BCrypt stores the number of rounds and the salt in the hash, you need to retrieve the correct hash first to perform the same hashing operation on the plaintext you are checking. Given this, you might as well just retrieve the row and perform the check, rather than retrieving the row, performing the hash, and then making another query.

  • Oliver Salzburg

    You syntax highlighter no longer seems to work

    • http://sunnyis.me/ Sunny Singh

      Yes I know, I’m in the process of building a new theme because my old one was lost. I’ll see to putting up syntax highlighting on the current one for now.