John Squibb
Password Hashing
Accepting passwords from users comes with a great deal of responsibility. As application builders, we are generally satisfied when a user provides the correct credentials to gain access to the sensitive data contained by the application. How we make this determination is generally of a magical nature to the end user, as they expect to provide the credentials, usually a username and password of some sort, to the application, which is assumed to be trustworthy.
It is our duty to protect the end user from attack by ensuring the passwords they provide do not fall into the hands of evil-doers in the event that our application ever becomes compromised. The best way to protect user passwords is through hashing, and today, we'll go over just how to do that.
Passwords are a Need-to-Know Basis
When users provide passwords, the application only needs to know that they provide the correct one. This can be done with simple string comparison. Presuming our application stores user data in a database, we'll look there for an appropriate match when the user provides a username and password. If they match, we grant them access to the top secret stuff, and go about our day. The content of the password string is arbitrary, so...
password = password
...is no different than...
5f4dcc3b5aa765d61d8327deb882cf99 = 5f4dcc3b5aa765d61d8327deb882cf99
...except that one contains much more entropy, at least in that a human cannot easily understand it.
How to Hash a Password
There are several built-in functions for hashing strings in PHP, such as md5(), sha1(), or stronger. There are pros and cons to each of the available methods, but these are beyond the scope of this article. The important thing is that the method we choose only work one-way, in that once we hash the password, we are unable to get the original from the hashed string. Therefore, something like mcrypt() or base64_encode simply will not do.
The following snippet shows how to encode a string using the sha1() function:
<?php
// Original password
$password = 'password';
// Hash that password!
$hashed = sha1($password);
// Now the password is:
// 5baa61e4c9b93f3f0682250b6cf8331b7ee68fd8
print $hashed;
This is pretty good, and we could chuck this in the database and call it a day, but we still lack decent entropy.
The problem is, that people tend to use common passwords: things in the dictionary, commonly-used words, kid's names, dates, and other easy-to-guess stuff. An attacker knows this, and they may employ a dictionary attack to try and brute-force commonly used passwords encoded with the known hash algorithms. We can thwart this process by adding some additional entropy to our passwords, in the form of a salt.
Using Salt on a Password
Salt is used to give passwords that extra bit of entropy, making it much more difficult to brute-force. There are countless ways to salt passwords, but whichever method we choose, one thing is certain: we have to store the salt. It is very important that the salt we use be kept in a safe place, or, for maximum security, that the salt be unique for each hashed password. We'll cover each method separately.
Using One Salt for All Passwords
Using a single salt for an application is acceptable. Consider the following modification to the earlier code:
<?php
// Original password
$password = 'password';
// Add some salt
$salt = 'SecretIngredient';
$salted_password = $password . $salt;
// Hash that password!
$hashed = sha1($salted_password);
// Now the password is:
// 1ad8b50cb8f1cdbd2536a1efa4ccffcbbe4302d0
print $hashed;
We now have a password that is a little tougher to guess, thanks to our salt. We could improve the algorithm and the salt a bit though:
<?php
// Original password
$password = 'password';
// Add some stronger salt
$salt = '__PPbdb10_MORE_SALTY!_3&%df9++*$&';
$salted_password = $salt . $password . $salt;
// Hash that password!
$hashed = sha1(sha1($salt) . md5($salted_password));
// Now the password is much more complex!
print $hashed;
For future use, we can place our hashing routine inside a function, and put it in a central location for easy access:
<?php
// Original password
$password = 'password';
print hash_password($password);
// Use a function to encapsulate the hashing method.
function hash_password($password)
{
// Add some stronger salt
$salt = '__PPbdb10_MORE_SALTY!_3&%df9++*$&';
$salted_password = $salt . $password . $salt;
// Hash that password!
$hashed = sha1(sha1($salt) . md5($salted_password));
return $hashed;
}
We can use this function to encode passwords on creation, and place them in the database. Later, when a user requests access, we can hash their supplied password and compare it to the password paired with their username in the database.
Using a Unique Salt for Each Password
Another approach is to use a unique salt for every password that is hashed. This helps prevent the birthday problem, in which a commonly used password will result in commonly appearing hashes when using the same salt for all passwords. Using unique salts requires us to keep track of each one we generate, so we'll store them along with their hashed password.
The following example embeds the salt of specified length within the password itself:
<?php
// Original password
$password = 'password';
$salt_length = 10;
// To create...
$hashed = hash_password($password, $salt_length);
// To check...
$check = hash_password
(
$password,
$salt_length,
substr($hashed, 0, $salt_length)
);
var_dump($hashed, $check);
/**
* Use a function to encapsulate the hashing method.
* Generate a random salt of optional length or supply
* the salt and length to generate hash to compare to
* known password.
*/
function hash_password
(
$password,
$length = 10,
$salt = NULL
)
{
// Generate random salt.
if (empty($salt))
{
$characters = str_split(md5(time()));
shuffle($characters);
$salt = implode($characters);
$salt = substr($salt, 0, $length);
}
// Salt and hash.
$salted_password = $salt . $password . $salt;
$hashed = sha1(sha1($salt) . md5($salted_password));
// Apply the salt to the password for access later.
return $salt . $hashed;
}
Now, we have a longer password with some decent entropy. It might seem a bit strange to apply the salt to the password when storing it, but keep in mind, we are more concerned with reducing the number of times the same password appears in the database as opposed to the security of the salt. We only have to safeguard the salt when we will be using the same one for all passwords we hash. When we create a unique salt for each password, we essentially have a throwaway salt.
Further Exploration
This article covers the tip of the iceberg of password hashing algorithms, entropy, and salting. Check out some of the following resources for a wealth of information regarding today's topic:
- PHP Security Consortium guide to password hashing
- Cryptographic hash function
- Hashing Algorithms
- NIST Computer Security Division
Read More
This article is one in a several-part series on PHP security. View all the articles
Suggested Reading
Mastering Regular Expressions
by Jeffrey E.F. Friedl
This is a must-read for anyone interested in learning the complexities of regular expressions usage.
Make no mistake, this book is not a quick reference, but rather an all-in tome of regular expression goodness. Even if you only use the
occasional regex to validate emails, phone numbers, credit cards, or other, this book will give you an excellent understanding of how
regular expressions perform such tasks.
Suggested Reading
High Performance MySQL
by Baron Schwartz et al.
High Performance MySQL is written by a collection of some of the best MySQL experts in the industry.
Check out their High Performance Blog. Their talent speaks for itself.
This book provides such a vast amount of information regarding MySQL, that it might take you a year or more to fully digest it all.
I suggest this book to anyone working with MySQL who would like to have a better understanding of performance, maintenance, server administration,
query building and optimization, or replication, as it is useful for MySQL administrators and developers alike.
I originally picked this book up at the suggestion of a Sun/MySQL employee, and continue to refer to and re-read this book regularly.
Suggested Reading
PHP 6 and MySQL 5
by Larry Ullman
I keep a copy of Visual Quickpro Guide, PHP 6 and MySQL 5 on my shelf to loan out to new PHP developers that have done little more than dabble in PHP for their personal website, and so on.
I literally wore an older edition of this book out by reading it from cover-to-cover, re-reading it, and carrying it around with me everywhere I went.
It was like my first guitar, I used the heck out of it! As the name implies, this book is designed to get you going. Larry Ullman does a great job of teaching the basics so that you can get a dynamyic website
talking to a MySQL database, complete with forms, sessions, cookies, security mechanisms, and on and on...If you don't know PHP, but are looking for a great way to dive in, buy this book.
Suggested Reading
PHP Objects, Patterns, and Practice
by Matt Zandstra
Apress' PHP Objects, Patterns, and Practice is great for beginners and veterans alike.
The first several chapters focus entirely on the foundations of Object-Oriented PHP (OOPhp), while later chapters teach various patterns such as the Factory, Singleton, Observer, and more.
The author, Matt Zandstra, places major emphasis on good programming habits and strives to teach as many enterprise level practices as space allows.
The last chapters introduce the reader to a series of productivity tools which cover version control, documentation, automated builds, and unit testing.
This text will complement any OOPhp application developer, and I recommend it to anyone looking to dive in, or a seasoned OOPhp developer looking to pick up some new tricks and tools.
Tags: Security, Passwords, Hashing, Salts
Short URL: http://sqb.in/12tn