Difference between #3 and #10 of Use crypt() for password storage

unchanged
Title
Use crypt() for password storage
unchanged
Category
Tutorials
unchanged
Tags
Authentication, security, password, hash, hashing
changed
Content
Storing passwords in web apps
----

> There is now a `CPasswordHelper` class in `system.utils` <a
href="https://github.com/yiisoft/yii/blob/master/framework/utils/CPasswordHelper.php">at
GitHub</a> 
> that provides an API to simplify the use of `crypt()` for password storage.

> While this wiki article remains valid, it will in due course be rewritten 
> to refer to the new class as well as explain how it works.

There are many tutorials and examples that show storage of passwords in a table.
Often the methods used are substandard and very easy to crack. There are many
web pages and tutorials that show how to do it wrong.

You cannot rely on a user to use a (practically) unguessable password or to not
use that password in systems other than yours. And you should not assume that
your systems are so secure that an attacker cannot get hold of the password
table or a backup of it. So you need to ensure that the password hashes in the
database are useless to an attacker. 

A very common error I see in what I read and other people's code is fast hashes.
MD5, for example, is very fast (as are all the SHA hashes). As of Nov
2011 you can check 350 million MD5 keys per second on a commodity nVidia
processor.
So no matter what you do with salts, the combination of short passwords and fast
brute force checking means your system is open to intruders if you rely on a
non-iterated message digest such as MD5 or any of the SHA algos. Most
hash functions are indeed designed to be fast to compute.

The Blowfish hash algorithm, on the other hand, is designed to be
computationally expensive and is currently considered pretty good for hashing
passwords. The implementation in PHP's `crypt()` is easy to use. Set a cost
parameter high enough to make a brute force attack really slow. I set it so that
it takes about 250 ms
on the production server which is fast enough for users to tolerate but slow
enough to
defeat a brute-force attack.

Each password should have a unique salt. The salt's purpose is to make the
dictionary size in a [rainbow table](http://en.wikipedia.org/wiki/Rainbow_table)
or [dictionary attack](http://en.wikipedia.org/wiki/Dictionary_attack) so large
that the attack is not
feasible. Salts used with the Blowfish hash [do not need to be
cryptographically
secure](http://security.stackexchange.com/questions/7193/cryptographic-security-of-dynamically-generated-non-random-salts/7195#7195)
random strings. A salt based on a decent pseudo-random number is sufficient to
defeat a rainbow table. 

Some people advocate re-salting every time a user logs in. I think this is only
useful if you also limit the time interval between user logins, e.g. by locking
out users that have not logged in for a long time. 

As computer speed increases with time, so does the attacker's chance of
succeeding with brute force. So if your software will be in use for many years
it should increase the Blowfish cost parameter in line with increases in
computer speed and rehash passwords next time the user logs on.


Using PHP's crypt() to store passwords
--------------------------------------

> If your PHP is older than 5.3, please read the section **Availability of
crypt()’s
Blowfish option** below.

People often get confused about how to use implement a password store using
`crypt()`.
It is actually very simple but it helps to know that:

* It is safe to store the salt together with the password hash. An attacker
cannot use
it to make a dictionary attack easier.

* The return value from `crypt()` is the string concatenation of the salt you
give it and the
hash.

* `crypt()` ignores excess characters in the input salt string.

The built-in PHP `crypt()` function's signature is:

* string **crypt** (string `$str`[, string `$salt`])
    
in which the
salt string's format determines the hash method. For a Blowfish hash, the format
is:
`"$2a$"`, a two digit cost parameter, `"$"`, and 22
characters from the alphabet
`"./0-9A-Za-z"`. The cost must be between `04` and `31`.

For example:

~~~
[php]
crypt('EgzamplPassword', '$2a$10$1qAz2wSx3eDc4rFv5tGb5t')
    >>	>>
'$2a$10$1qAz2wSx3eDc4rFv5tGb5e4jVuld5/KF2Kpy.B8D2XoC031sReFGi'
~~~

Notice that the first 29 characters of the returned value are the same as the
salt string.


Append anything to the salt string argument and the result is unchanged:

~~~
[php]
crypt('EgzamplPassword', '$2a$10$1qAz2wSx3eDc4rFv5tGb5t12345678901234567890')
    >>	>>
'$2a$10$1qAz2wSx3eDc4rFv5tGb5e4jVuld5/KF2Kpy.B8D2XoC031sReFGi'

crypt('EgzamplPassword',
'$2a$10$1qAz2wSx3eDc4rFv5tGb5t$2a$10$1qAz2wSx3eDc4rFv5tGb5t')
    >>	>>
'$2a$10$1qAz2wSx3eDc4rFv5tGb5e4jVuld5/KF2Kpy.B8D2XoC031sReFGi'
~~~

And in particular, passing the value returned from `crypt()` back in as the salt
argument:

~~~
[php]
crypt('EgzamplPassword',
'$2a$10$1qAz2wSx3eDc4rFv5tGb5e4jVuld5/KF2Kpy.B8D2XoC031sReFGi')
    >>	>>
'$2a$10$1qAz2wSx3eDc4rFv5tGb5e4jVuld5/KF2Kpy.B8D2XoC031sReFGi'
~~~

Thus we can use `crypt()` to authenticate a user by passing the hash value it
gave us previously back in as a salt when checking a password input. It is
fiendishly simple:

**Create new hash**

~~~
[php]
$hash = crypt($password, $salt)
~~~

**Validate password against a stored hash**

Compare the strings `$hash` and `crypt($password, $hash)`.

**NOTE:** We should use a constant-time string comparison algorithm to defeat 
the possibility of 
[timing
attacks](http://blog.astrumfutura.com/2010/10/nanosecond-scale-remote-timing-attacks-on-php-applications-time-to-take-them-seriously/)
learning the result of validation. See below for an example.~~~
[php]
$hash === crypt($password, $hash)
~~~


## Generate a Blowfish salt

The following function will generate a salt suitable for use with `crypt()`.

~~~
[php]
/**
 * Generate a random salt in the crypt(3) standard Blowfish format.
 *
 * @param int $cost Cost parameter from 4 to 31.
 *
 * @throws Exception on invalid cost parameter.
 * @return string A Blowfish hash salt for use in PHP's crypt()
 */
function blowfishSalt($cost = 13)
{
    if (!is_numeric($cost) || $cost < 4 || $cost > 31) {
        throw new Exception("cost parameter must be between 4 and
31");
    }
    $rand = array();
    for ($i = 0; $i < 8; $i += 1)++$i) {
        $rand[] = pack('S', mt_rand(0, 0xffff));
    }
    $rand[] = substr(microtime(), 2, 6);
    $rand = sha1(implode('', $rand), true);
    $salt = '$2a$' . str_pad((int) $cost, 2, '0', STR_PAD_RIGHT) . '$';
    $salt .= strtr(substr(base64_encode($rand), 0, 22), array('+' => '.'));
    return $salt;
}
~~~



Example
-------

Say I have a `user` table like this

~~~
[sql]
create table user (
    id	id int not null auto_increment primary key,
    username
	username varchar(255) not null,
    password_hash
	password_hash char(64) not null,
    unique
	unique key (email)
)
~~~

First, consider processing the form to create a new user acount. I have (already
sanitized) form input in `$form->email` and `$form->password`. I can
generate the hash from the password and a salt from the `blowfishSalt()`
function above:

~~~
[php]
$passwordHash = crypt($form->password, blowfishSalt());
~~~

I can insert a row into `user` containing `$form->username` and
`$passwordHash`.

When a user submits a login form, I have sanitized form input in
`$form->username` and `$form->password`. To authenticate these inputs I
select the record from the `user` table with matching username loading the
password hash into `$record->passwordHash`

~~~
[php]
if ($password_hash === crypt($form->password, $record->passwordHash))
    //	// password is correct
else
    //	// password is wrong
~~~

So there is no need to store the salt in a separate column from the hash value
because
`crypt()` conveniently keeps it in the same string as the hash.



In a Yii webapp
------

Using `crypt()` requires very little code. Just one line in user registration
and one in authentication plus the `blowfishSalt()` function from above.

### Registration

In a Yii webapp I usually have an Active Record model class `User` for the user
records in the `user` DB table. In this example I have a controller action that
processes the website's new account generation form. The form input is in
`$form`, a `CForm` model instance with attributes `username` and `password`.
Assume the model validated OK.

The controller action where you register new users might include:

~~~
[php]
$user = new User;
$user->email = $form->email;
$user->password = crypt($form->password, self::blowfishSalt());
if ($user->save()) {
    ...
}
~~~

This assumes I put the `blowfishSalt()` function from above is a static method
of this controller class.

### Authentication

To authenticate a user at logon, I follow the [auth topic in the Yii
Guide](http://www.yiiframework.com/doc/guide/1.1/en/topics.auth) and write the
`UserIdentity::authenticate()` method as follows. In a default `yiic webapp`
this `authenticate()` is in `protected/components`.

~~~
[php]
public function authenticate()
{
    $record = User::model()->findByAttributes(array('username' =>
$this->username));
    if ($record === null) {
        $this->errorCode = self::ERROR_USERNAME_INVALID;
    } else if ($record->password !== crypt($this->password,
$record->password)) {
        $this->errorCode = self::ERROR_PASSWORD_INVALID;
    } else {
        $this->_id = $record->id;
        $this->setState('title', $record->title);
        $this->errorCode = self::ERROR_NONE;
    }
    return !$this->errorCode;
}
~~~

## Constant-time string comparison.

This function is based on 
[this](http://codereview.stackexchange.com/questions/13512/constant-time-string-comparision-in-php-to-prevent-timing-attacks)

and 
[this](https://github.com/ircmaxell/password_compat/blob/master/lib/password.php).

~~~
[php]
function same($a, $b) {
    /**
     * @see http://codereview.stackexchange.com/questions/13512 
     */
    if (!is_string($a) || !is_string($b)) {
        return false;
    }
    $mb = function_exists('mb_strlen');
    $length = $mb ? mb_strlen($a, '8bit') : strlen($a);
    if ($length !== ($mb ? mb_strlen($b, '8bit') : strlen($b))) {
        return false;
    }
    $check = 0;
    for ($i = 0; $i < $length; $i += 1) {
        $check |= (ord($a[$i]) ^ ord($b[$i]));
    }
    return $check === 0;
}
~~~


## Availability of `crypt()`'s Blowfish option

The `crypt()` function has ben part of PHP for a long time but not all PHP
installations
have all its options.
I use the Blowfish hash option which is available in all PHP systems since
5.3.0.
It is also available in pre-5.3 PHPs (including PHP 4) if the operating system
has the Blowfish hash option in
its standard library
[`crypt(3)`](http://www.freebsd.org/cgi/man.cgi?query=crypt&sektion=3)
function, as many *nix systems do. Failing this, the [Suhosin
patch](http://www.hardened-php.net/suhosin/index.html) will provide the Blowfish
hash in an old PHP system.

PHP's `CRYPT_BLOWFISH` constant is `true` if and only if the system has
Blowfish.

It can
be tricky to implement good password hashing on PHP systems that do not have
Blowfish in `crypt()`. I do not have any recommendations other than to upgrade
your PHP or 
move to a host with an up-to-date PHP.

Be careful of slow hash function implementations in PHP. The important thing is
that the hash takes a lot of compute time *on the attackers equipment* and takes
the minimum possible on yours. A hash implemented in PHP puts you at a
disadvantage relative to the attacker. For example, imagine you iterate
SHA-1 in in PHP for one second on a Micro instance on Amazon EC2 while the
attacker has the same algorithm optimized to run on $5k's worth of modern GPUs.
I don't know how many orders of magnitude faster the attacker's algorithm is
than yours but I think its enough so that you should feel unsafe with such an
implementation.