Creating a Secure Hash in PHP

When creating a web application with users it is always important to securely hash their password. We are going to go over how to create a hash that is impossible to brute force.

We’ll assume that this script is open to the public and anyone can see the algorithm we use – so how do we make it uncrackable and non-bruteforcable?


The answer is randomly generated salts. We’ll randomly generate a salt, store it in a database along with the username, hashed password and any other user associated information, and then generate their hashed password based off of the salt and password.

When we check to see if they have a valid username/password we will check the database for the username the user supplied (via cookies or when logging in), pull out the password salt, hash the password that they have given with the salt and see if it fits the hash in the database.

The Code

First we’ll need to create a database (we’ll be using MySQL) and create a table within it. We’ll call the database “app” and the table “users”. For our intents and purposes we will only have three fields: the user ID, the username, and the hashed password. The user ID field will be auto incrementing. To create the table we will execute the following SQL:

CREATE TABLE `app`.`users` (
`uid` INT NOT NULL AUTO_INCREMENT ,
`username` TEXT NOT NULL ,
`hash` TEXT NOT NULL ,
`salt` TEXT NOT NULL ,
PRIMARY KEY ( `uid` )
)

Now we’re going to create a function which will register the user, first hashing the inputted password. We’re going to use the uniqid() function to generate the salt. To create the hash based off the salt we’ll use crypt(). Here’s the code:

function register($user, $password)
{
    $salt = uniqid(mt_rand()); // Create a salt based off the time, prefixed with a random number.
    $hash = crypt($password, $salt); // Generate the hash.
}

Next in the function we insert the data into our table.

// We'll escape the input before this

$con = mysql_connect("localhost","peter","abc123"); // Fill in database server, username and password
if (!$con)
{
  die('Could not connect: ' . mysql_error());
}

mysql_select_db('app', $con);

mysql_query("INSERT INTO users (username, hash, salt)
VALUES ('$user', '$hash', '$salt')");

mysql_close($con);

All together now:

function register($user, $password)
{
    $salt = uniqid(mt_rand()); // Create a salt based off the time, prefixed with a random number.
    $hash = crypt($password, $salt); // Generate the hash.
    // Prepare the data for the database
    $salt = mysql_real_escape_string($salt);
    $hash = mysql_real_escape_string($hash);
    $user2 = mysql_real_escape_string($user);
    $con = mysql_connect("localhost","peter","abc123");
    if (!$con)
    {
       die('Could not connect: ' . mysql_error());
    }
    mysql_select_db('app', $con);
    mysql_query("INSERT INTO users (username, hash, salt)
    VALUES ('$user2', '$hash', '$salt')");
    mysql_close($con);
}

Now we need a function which checks to see if the user supplied info (which would normally be stored in cookies or used during login) is valid. We’ll need to know their inputted username and password.

First we’ll connect to the database and find the salt and password hash given the username. To do this we’ll use the following SQL:

SELECT hash, salt
FROM users
WHERE username = '$username'
function validateUser($username, $password)
{
    $user = mysql_real_escape_string($username);
    $con = mysql_connect("localhost","peter","abc123");
    if (!$con)
    {
       die('Could not connect: ' . mysql_error());
    }
    mysql_select_db('app', $con);
    $info = mysql_query("SELECT hash, salt FROM users WHERE username = '$user");
    $row = mysql_fetch_row($info);
    mysql_close($con);
}

Now that we’ve selected the proper row, the hash is stored in $row[0] and the salt is stored in $row[1]. For clarity’s sake we’ll assign them to $hash and $salt.

$hash = $row[0];
$salt = $row[1];

Next we’ll hash the password using $salt and check to see if it matches $hash.

$hashCheck = crypt($password, $salt);
if ($hashCheck == $hash)
{
    // The user provided a valid username and password.
}

And the final function:

function validateUser($username, $password)
{
    $user = mysql_real_escape_string($username);
    $con = mysql_connect("localhost","peter","abc123");
    if (!$con)
    {
       die('Could not connect: ' . mysql_error());
    }
    mysql_select_db('app', $con);
    $info = mysql_query("SELECT hash, salt FROM users WHERE username = '$user'");
    $row = mysql_fetch_row($info);
    mysql_close($con);
    $hash = $row[0];
    $salt = $row[1];
    $hashCheck = crypt($password, $salt);
    if ($hashCheck == $hash)
    {
        // The user provided a valid username and password.
    }
}

There you have it! A secure way to create and verify password hashes in PHP, perfect for any user system. I hope you enjoyed this article and feel free to leave a comment!

This entry was posted in PHP. Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

12 Comments

  1. Someone
    Posted July 12, 2010 at 10:44 PM | Permalink

    Please please please learn about SQL injection and PDO with prepared queries to avoid it.

    If you already know then PLEASE fix the above to show correct binding and avoid this issue, as I am certain that someone is going to copy paste this, loose their database and wonder why.

    • Posted July 13, 2010 at 1:16 PM | Permalink

      I added mysql_real_escape_string(), however I kind of assumed that the coder would pass the data already escaped or figure out how to do it himself. My mistake.

    • Someone
      Posted July 13, 2010 at 6:19 PM | Permalink

      It would still be better to use PDO and prepared queries. mysql_connect etc.. is quite old and should be removed from PHP. mysqli is better, but PDO is MUCH better since it prevents you having to even think about it and you can change database whenever you require.

    • Posted July 13, 2010 at 8:39 PM | Permalink

      Though I have to agree with you that using something like PDO or a database abstraction layer like Doctrine would be optimal I’m just trying to go over the process, this can be fairly easily ‘translated’ into other databases too.

    • Someone
      Posted July 15, 2010 at 10:41 PM | Permalink

      Perhaps the example without the database connection would be better then. I see a lot of insecure code in the PHP space and its certainly a bad way for things to be.

  2. Posted July 13, 2010 at 12:33 PM | Permalink

    One of the ideas of using salt to store passwords is to make harder to someone discover weak passwords using dictionary attacks. I don’t think it’s a good idea store the salt along with the passwords. If someone get access to your database he can try this approach to get the passwords that are common words or numbers.

    • Posted July 13, 2010 at 1:03 PM | Permalink

      But if someone is able to get access to your database you have far bigger problems.

    • Someone
      Posted July 13, 2010 at 6:18 PM | Permalink

      It doesn’t matter. As Andrew mentioned, at that point its game over. Salting just makes it almost impossible to run the hash through a rainbow table and find the password assuming they can get the hash somehow. In fact you can even store the salt on the end of the password hash and pull it apart through string manipulation if you want.

  3. Posted July 16, 2010 at 8:56 AM | Permalink

    I’m a little surprised no one else has picked up on this…. Near the end of your final function…

    if ($hashCheck = $hash)

    Should be….

    if ($hashCheck == $hash)

    The original version (with a single =) wll perform an assignment making $hashCheck the same value as the stored hash. It will evaluate to the value of $hash which when typecast as a boolean will almost always be true (see Boolean Data Type in the PHP docs for details). In a nutshell, an = instead of == will basically allow the user to log in no matter the password.

    However, the thrust of the article is good. There’s no danger in storing the salt in the database along with the hashed password, the Unix shadow password file has done it that way for decades now.

    Jim.

    • Posted July 16, 2010 at 11:47 AM | Permalink

      Good catch! I updated the article fixing this.

  4. erb
    Posted October 26, 2011 at 5:11 AM | Permalink

    aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

  5. Posted April 9, 2012 at 6:14 AM | Permalink

    This is really attention-grabbing, You’re an overly skilled blogger. I have joined your feed and look forward to in quest of more of your wonderful post. Additionally, I’ve shared your web site in my social networks

2 Trackbacks

  1. [...] این مطلب روشی جالب در این زمینه ارائه میکند که از دو تابع uniqid (برای تولید salt ) و crypt برای برای encryption آن استفاده میکند… [...]

  2. By 網站製作學習誌 » [Web] 連結分享 on July 20, 2010 at 12:08 AM

    [...] Creating a Secure Hash in PHP [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>