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.

<?php

$hash = md5(“the password”);

?>

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.

<?php

require(“PasswordHash.php”);

?>

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.

<?php

$hasher = new PasswordHash(8, false);

?>

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.

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

}

?>

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.

<?php

require(“PasswordHash.php”);

$hasher = new PasswordHash(8, false);

?>

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.

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

}

?>

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.

  • piet hermans

    I´d like to ask whats the best way to manage the login result throughout further navigation.
    The user loged in Ok. The he´s taken to some page, as from then, is it secure to use (php) session variable, or cookies (in which case what information should be stored in the cookie and how is it to be validated at each new page). ? thanks.

    • http://www.nikrolls.com/ Nik Rolls

      You only need to do the password check once. It’s perfectly secure to use the Session to store the data — this is not accessible outside the server.

      Note that depending on your framework there may be techniques where a malicious user could spoof the session id and get access to someone else’s session; this is very unlikely and they still can’t see the raw data, but it is technically possible. Because of this it’s best practice to ask the user to enter their password again if they want to do anything significant like changing their email or password, making a purchase, closing their account, etc. This is also good practice in case someone else has jumped onto their computer while they’re still logged in.

      • piet hermans

        Hey, thanks a lot Nik. :-)
        Finally I tried a cookie arrangement using this format:
        user name|expiration time|(data)k|HMAC( user name|expiration time|data|session key, k)
        where k=HMAC(user name|expiration time, sk)
        and where sk is a secret key

        which I found at: http://core.trac.wordpress.org/ticket/5367#comment:29

        To which I added an IP control. (Just testing posibilities and finding out what will work for me)…
        But as you say, maybe just the open session will be more than enough for most cases. Thanks again.

        • http://www.nikrolls.com/ Nik Rolls

          IP checks are good; obviously sometimes these hacks can come within the same IP, but it’s much less likely. Because you’re using an IP check you’d definitely want to give an option to remain logged in for users whose IPs change often.

          I personally would hesitate to save any user details in a cookie (even a username), because it’s less than impossible to be accessed maliciously. I’m probably being a little over-the-top as the difficulty is fairly high and the potential leak from a username fairly low, however in my mind less client-side data is safer and since it’s easier to use server sessions than implementing a custom cookie system then you may as well save time and reap the added security benefits.
          Big disclaimer though is that I know nothing about your setup and requirements — I’m sure that if you’re pursuing it then there’s a good reason :)

          • piet hermans

            Thanks again Nik, I appreciate very much your taking the time and trouble to answer. It´s been of great help. I´m considering your ideas and will probably make further changes, for this safer and simpler approach.

          • http://www.nikrolls.com/ Nik Rolls

            Most welcome, all the best :)

  • imtiaz

    could you be more specific as to how to install it and use it. i have downloaded it, extracted to where all my php files are on localhost – but it doesnt work becuase it refers to some other directory???

    also what do i do with the PGP key?? how do i use it? do i need to use it?

    it seems you have be profiled highly when accessing / googling phpass vai the openwall.com website – it’s fair that you explain all in step-by-step detail.

    thank you.

    • http://sunnyis.me/ Sunny Singh

      Once you download the zip file, all you need is the PasswordHash.php file. As I stated in the article, you use require(“PasswordHash.php”); to include the framework and start using it. This is assuming that it’s in the same directory, but if not you may need to change it to something like require("some_directory/PasswordHash.php").

      As far as I know, you don’t need to worry about the PGP key. If you still have problems installing it, let me know. I think that it’s simply an error with where you’re including the file from.

  • imtiaz

    help!
    i get this error on my final check after retrievng stored password and calling for a checK:

    Fatal error: Call to a member function CheckPassword() on a non-object in mydirloginscript.php on line 226

    my code is:

    require(“./PasswordHash.php”);

    $password = $pwtocheck; //– password from ligin
    $stored_hash = $row[‘encpass’; //– db stored pw

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

    • gchrz

      You seem to be missing the closing bracket on

      $stored_hash = $row['encpass']; //– db stored pw.

      hope this helps

  • imtiaz

    sorry.. ignore my two earlier post .. i have made some progress. thank god.

    just need last bit of help:

    PROBLEM is comming up at final check as error issued as failed:
    [ passwords didn't match, show an error ]
    ** but password input [txt at sign-up + login are idential.

    question.
    does the checkpassword() function hashes the password by itself before comparing it to the $stored_hash that is supplied to it or am i missing something else??
    thanks.

  • ELQ

    Used these instructions, as well as the ones on the creator’s site multiple times..so I’m hoping somebody will have a suggestion as to why this:

    for($i=0;$iHashPassword($password); //hashed password
    print($i.”)  ”.$hash.”");
    $hash = null;
    }

    results in this:

    0) $2a$08$mQuSL/WfrqeCdX4.SUWRxe2QtLAx5hDdl/U9b2SolHjIwLCWY6FR6
    1) $2a$08$fSejXXdqFZWgjF95q7IZ/.3NMSktwZzCViOf5QdkCfbVkGQzhwOUS
    2) $2a$08$yGIQyoeNepTGNi3KenivKeSo97nyzFCs7t2nAkyBl7OSy10Kq2Aey
    3) $2a$08$G9suVtW8CbDBLyU07X6Ljuibk8bppT3K4c5m.uS/u37yWebeN.NgS
    4) $2a$08$pLK9.5tEeN6n/OXmxSu/xOzNeF0pp4ttVsw0PavebF1K3t3Tw66ZW
    5) $2a$08$cdpRxo8vzXTbN6tU21asCuOj55e16H8H0KTVqsR4ToKhbCeszigQ2
    6) $2a$08$xp967jUv/9nsWn.3RKcRSO41LLE5ijve0BqVH4F.078KfiWUwmDhS
    7) $2a$08$Qi/DI3pvveDb0I9nhgP6MuZDsnsicfxwsSavjHgcvMhQiJaCJGyW.
    8) $2a$08$hD66R/358dQfvnmgtjWMn.8PPxdK/0BG56Xev9uyo/Oeosg5NjsqG
    9) $2a$08$hx4toSBl7YUozjMFp2Rnn.STIUUVLdurtAEN1To3F0g2LFkCcqWrm
    10) $2a$08$DcLjGH/zh9UyNHPYVa7iEe4Zq/yyjKrwGmdrt5aq16oWYTy4p3Etq
    11) $2a$08$KGxmw9OxVXqiNr.9dDls7upT43t9786flx43eqcDGhcSV1JhaalDG
    12) $2a$08$o4z0M2J0sCItgqyOY668seBhTVwVnWs4WrmrrUPW4DDjJVHlAB1HG
    13) $2a$08$/5U2D/EyByU2fsXRsFfKKe4X4UOMi2CR3nZQlU/MRHLmj.CRKZDQO
    14) $2a$08$a6AMuuCXi7zbpZ5hXlCi4evBPgFRqPGhftvxvwliqXdp5wyKmIptq
    a shiny new hash everytime and never a match for the one stored in the database from the first time :(

    • http://shankar96.net/ shankar96

      That gives you a new hash every time because you are hashing a password, and a new salt is used every time a password is hashed, resulting in a new hash every time.

      If you need to check the password, use hasher->CheckPassword($password, $stored_hash);

  • Vinayak Mahadevan

    I tried using the example given in your site and the password is getting hashed. But when I try to use the checkpassword function it neither returns true nor false. It just returns a empty string

  • jon chambers

    Hi.

    I can hash the password, but am having trouble checking a stored hashed password. What sort of statement would you use to retrieve the stored password, in order to compare with the submitted one?

    I have something like this:

    $stored_hash = mysql_query(“SELECT password FROM Users WHERE username = $user”);

    I created a user variable that is populated by a form ($user = $_POST["user"];), so i thought this might work.

    Thanks!

  • Ajure

    I am trying to use phpass to have a login/password form based authentication for some part of my web site. There are lot of examples (6 demo directories I think) available on its website. However, none of them shows add a user/password manually, i.e., adding user/pass to mysql database from Ubuntu terminal (possibly) or any other way (phpmyAdmin??). I do not want to have a register button on my web site. Any tips will be greatly helpful.