User Security lab

Getting Started

In this lab, you're going to make a better User model with password hashing. Check out the empty repo to start, and create a new directory to work in on your VM. Linking a new directory to the webserver:

etomai@lamp-6312-vm:~$ mkdir assn08
etomai@lamp-6312-vm:~$ sudo ln -s ~/assn08 /var/www/html/assn08

Repo invite: https://classroom.github.com/a/giCdAeoT

Data First

For this assignment, we need to update our database user table with a password field. Using myPhpAdmin, add the following column to the user table:

password_hash: varchar(128)

Model Next

Create (or copy) your composer.json file and use composer to install propel in this project. You have to edit out the autoload section in composer.json or it will error and corrupt the install trying to load models that aren't there yet.

Then, generate your models with propel init. Afterward, put the autoload section back in composer.json and run composer dump-autoload.

In a new php file, do a quick check to make sure that your User model is properly configured and works.

  • Add a new User with bogus data
  • Echo out the added id
  • Retrieve that user by username
  • Echo the user object

Load the page to see if it works, and check your database to confirm that the test user was added.

Hashy

In this part, we're going to take advantage of the fact that Propel creates a stub User model (in models) that inherits from the Base/User model. That stub allows us to add functionality. In this case, we're going to override setPasswordHash to take the provided password, hash it, and store the result in the database (instead of storing the actual password).

PhPassLib

Never roll your own security code! We have a nice option here with the simple PhPassLib hashing library, which we can install using composer and autoload. Update your require section of your composer.json to include:

 "require": {
        "propel/propel": "~2.0@dev",
        "rych/phpass": "3.0.*@beta"
    }

Then run composer update to install the new library and composer dump-autoload. Autoload is a bit overly complex and opaque, but you see the advantages here for a project that uses many dependencies. Since we're already using require_once "vendor/autoload.php"; in our Controller, we're all set to use PhPassLib as well.

Comment out your insert code for a minute and run this simple example in your php file to see how PhPassLib works.

$password = "horriblepassword";

// Calculate a bcrypt hash from a password
$hash = PHPassLib\Hash\BCrypt::hash($password);

echo "First hash is: ".$hash."<br/>";

// Do it again!
$hash = PHPassLib\Hash\BCrypt::hash($password);

echo "Second hash is different (salt changed): ".$hash."<br/>";
 
// Calculate a bcrypt hash with a specific number of rounds
$hash = PHPassLib\Hash\BCrypt::hash($password, array ('rounds' => 16));

echo "Third hash is sloooow: ".$hash."<br/>";
 
// Check supplied password against a stored hash
if (PHPassLib\Hash\BCrypt::verify($password, $hash)) {
    echo "Password is valid!<br/>";
}

Storing Hashes

Now your part! Add a setPasswordHash method to the User model to override Base\User::setPasswordHash. Your method should:

  1. Take a password string as its parameter
  2. Use PhPassLib to hash that password
  3. Call the Base\User setPasswordHash method to store the hash string instead of the actual password string

Note, in PHP you use parent::method_name() to call an overridden parent method.

Now uncomment and re-run the insert test in your php file. Check your db - you should be storing users with real password hashes.

Login

Finally, update the User model to add a login method. Comment out your testing and insert code and paste in the following and make it work.

// set $username and $password to a valid combination

$user = UserQuery::create()->findOneByUsername($username);
echo "Got user: ".$user."<br/>";

echo "Login with correct password...<br/>";
if( $user->login($password) ) {
   echo "Success!<br/>";
} else { 
   echo "Failure!<br/>";
}

echo "Login with incorrect password...<br/>";
if( $user->login("bogusbogusbogus") ) {
   echo "Success! (that's bad)<br/>";
} else { 
   echo "Failure! (that's good)<br/>";
}

Turning In

Commit the php file you tested with, with the testing, insert and login code in it (just keep it neat and comment our parts you're not using), as well as your modified User.php model.