[[more-secure-passwords-with-php]]
More Secure Passwords with PHP
------------------------------

[source,javascript]
----
function thest() {
    alert('hello');
}
----

Posted on Wednesday, 4th September 2013 by admin

Update 2: This article has been superceeded by a more to-the-point
article which focuses more on using the newer functions.

Update 1: PHP 5.5 implements new password functions to simplify working
with passwords. Even if you are using an earlier version of PHP, you
should look at these functions. You can learn more at the end of this
article.

Website owners who maintain user details have a great responsibility.
Apart from keeping the database safe and sound, you will have to ensure
that user passwords are kept safe and secure.

A separate article will discuss protecting your database from SQL
injection attacks using prepared statements. This article will discuss
making passwords harder to crack.

Many sites, some which should have known better, have had their user
databases compromised. This is bad enough, but if the miscreants get
their evil hands on user passwords, this may allow them to attack
accounts held on other sites.

[[for-users]]
For Users
~~~~~~~~~

From a user point of view, these are the practices you need to follow:

* Have a different password for each online account. Use some sort of
secure password safe to help keep track of all of your passwords.
* Always change your password if you have learned that the website has
been compromised.
* Check the password policy and recovery procedure. If the password is
limited in length or in which characters you may use, or if passwords
can be recovered, then this is a sign that the password is stored
insecurely. If so, complain or consider taking your business elsewhere.
At least be very very careful.

On that last point, a password policy which enforces adding complexity
is a good thing. A password policy which forces simplification is a bad
thing. And password recovery, as you will see later, should be
impossible in a secure system.

[[password-storage]]
Password Storage
~~~~~~~~~~~~~~~~

To put it simply, passwords should never be stored. That is, in any
helpful format. Instead, a hashed copy of the password should be stored.

Hashing is similar to encryption in that it produces a scrambled version
of the original. Where it differs is that the process is irreversible.
You cannot unhash a string. Encryption, on the other hand, is designed
to be reversible given the correct key, and is useful for transmitting
messages in secret.

The point to hashing is that the process is

There are many ways of hashing strings, but, in general, you need a
method which:

* can be implemented in PHP
* hasn’t been cracked
* takes long enough to slow down a cracker without blowing all of your
resources

Most modern algorithms available in PHP are OK, though MD5 has
apparently been cracked. However, to complicate things, we also have to
consider brute force.

[[salts]]
Salts
~~~~~

Hashing algorithms such as SHA have not been cracked, but they are
popular enough that tables exist of potential passwords and their
hashes. Such tables are often referred to as rainbow tables.

This is one reason why a good password is not short and contains a
mixture of upper and lower case, numbers and other special characters:
it is less likely to have appeared in such a table. Character
substitutions, such as `p@55w0rd` are passé, and have already been
included.

Passwords are also subject to brute force attacks. That is, given enough
power, storage and time, it should be possible to try every combination
until something matches. Modern computers have enough speed and power to
reduce the time required to just a few seconds. Again, a good password
makes things harder, but not impossible.

A salt is an artificial string added to your password. This adds to the
length and complexity of the password, and makes it less likely to make
an appearance in an existing list. It should be a random string, and not
reused for other passwords. It doesn’t have to be secret, as, by itself,
it reveals no useful information.

[[user-registration]]
User Registration
~~~~~~~~~~~~~~~~~

Typically a user does the registration, but what follows also applies if
the registration is performed by the administrator.

First, there is a database table which includes the user name, hashed
password, and a few more details, usually including the email address.
If you ensure that the email address is unique, you can use the email
address as a user name, simplifying the process.

It is also a good idea to have a password expiry date. This is not so
much as to force password changes, but to allow for temporary passwords.

The SQL to create such a table would be something like this:

[source,sql]
-------------------------------------------------
CREATE TABLE users (
	id INT UNSIGNED PRIMARY KEY,
	email VARCHAR(64) UNIQUE INDEX,
	passwordhash CHAR(40), -- for use with sha1()
	passwordexpires DATETIME,
	-- more fields
);
-------------------------------------------------

The `passwordhash` field is a character field fixed to 40 characters,
which the output you get from the `sha1()` function.

The PHP to add a new user to the table would look as follows:

[source,php]
-----------------------------------------------------------
$sql='INSERT INTO users(email,password,etc) VALUES(?,?,?)';
$prepared=$pdo->prepare($sql);
$data=array($email,sha1($password),$etc);
$prepared->;execute($data);
-----------------------------------------------------------

This uses a prepared statement which protects against SQL injection. The
etc data is, of course, a place holder for the rest of the user data.

Using the above technique, you have stored a hashed version of the
password which cannot be reversed. It might, however, be susceptible to
rainbow or brute force attacks, in which case you might prefer to
include a salt.

For the table, add a salt field:

[source,sql]
-------------------------------------------------
CREATE TABLE users (
    -- etc
    passwordhash CHAR(40), -- for use with sha1()
    passwordsalt CHAR(20), -- or whatever length
    -- etc
)
-------------------------------------------------

For the PHP, concatenate the salt and add the salt and the hashed string
to the table.

[source,php]
---------------------------------------------------------------------
$salt='....................';   //  put in some real random text here
$sql='INSERT INTO users(email,password,salt,etc) VALUES(?,?,?)';
$prepared=$pdo->prepare($sql);
$data=array($email,sha1($salt.$password),$salt,$etc);
$prepared->execute($data);
---------------------------------------------------------------------

[[authentication]]
Authentication
~~~~~~~~~~~~~~

The main purpose of passwords is, of course, authentication. That is, to
allow a user to log in using their user name and password.

Since the password is not to be stored directly, you will need to
compare not the passwords, but the hashed version. Without the
complication of salting, the following PHP would do the job:

[source,php]
---------------------------------------------------------------
$email='…'; //  From login
$password='…';  //  From login
$sql='SELECT count(*) FROM users WHERE email=? AND password=?';
$prepared=$pdo->prepare($sql);
$data=array($email,sha1($password));
$prepared->execute($data);
if($prepared->fetchColumn()) {
    //  successful
}
else {
    //  no good
}
---------------------------------------------------------------

Note that the password is hashed before it is compared. In this way the
original password need not be stored in order to verify the match.

If you’re using a salt, it gets a bit more complicated. You first need
to read the individual salt from the table, and then include the salt
when hashing the password for comparison. This involves reading the
record twice, once for the salt, and again for the comparison:

[source,php]
-------------------------------------------------------------------
$email='…'; //  From login
$password='…';  //  From login
//  get salt
    $sql='SELECT salt FROM users WHERE email=?';
    $prepared=$pdo->prepare($sql);
    $data=array($email);
    $prepared->execute($data);
    $salt=$prepared->fetchColumn();
//  salt &amp; check password
    $sql='SELECT count(*) FROM users WHERE email=? AND password=?';
    $prepared=$pdo->prepare($sql);
    $data=array($email,sha1($salt.$password));
    $prepared->execute($data);
if($prepared->fetchColumn()) {
    //  successful
}
else {
    //  no good
}
-------------------------------------------------------------------

It might be possible to simplify the query query to a single SELECT
statement by relying on the database’s own `sha1()` function, but that
is less flexible. It might look like this:

[code,sql]
--------------------------------------------------------------------------
SELECT count(*) FROM users WHERE email=? AND password=sha1(concat(salt,?))
--------------------------------------------------------------------------

[[using-php-crypt]]
Using PHP `crypt()`
~~~~~~~~~~~~~~~~~~~

Some of the many improvements in newer versions of PHP include password
security. The crypt() function has been around for a long function, but
as of 5.3 it adds some improvements.

The `crypt()` function allows you to choose from a number of alternative
hash algorithms. One particular algorithm, known for some reason as the
blowfish algorithm, offers a better hashing, as well a variable cost.
There are possibly even better algorithms available, but this will do
for the purposes.

The cost of the blowfish algorithm is an interesting property. It
indicates the number of iterations required to complete the hash.
Increasing the cost means that it will take longer, possibly much
longer, to compute hash, and can, if high enough, take a number of
seconds to complete. The idea is that for a legitimate login, the extra
time is barely noticeable, for for someone trying to brute force the
password, it could take millions of seconds, which could be a matter of
months.

In PHP, the `crypt()` function requires the password, of course, and
possibly a salt, which is the whole point to this exercise.

The salt is a string of 29 characters, which is a combination of 7
characters of instruction and 22 characters of (hopefully) random text,
from a range of 64 acceptable characters. The instruction characters
encode the blowfish variation as well as the cost.

To add a password to the database:

* Generate a salt
* Use `crypt()` to generate the hash
* Add the result to the database

[source,php]
------------------------------------------------------------------------------------
//  Get the user’s password
//  Create Initial Salt
    $salt='';
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    for($i=0;$i&lt;22;$i++) $salt.=$chars[rand(0,63)];
    $salt=sprintf('$2a$10$%s',$salt);   //  preferably '$2y$10$…' if PHP &gt;= 5.3.7
//  Generate the Hash
    $dbpassword=crypt($password,$salt);
//  Add to the database …
------------------------------------------------------------------------------------

The interesting thing about this method is that if you later pass the
hashed password as the salt parameter, you will get get the same result
as the as the hashed password. That is, you can test:

[code,php]
-----------------------------------------
crypt($password,$dbpassword)==$dbpassword
-----------------------------------------

This gives us the following test:

[source,php]
---------------------------------------------------
//  Get password from database
//  Test by re-hashing:
    if(crypt($password,$dbpassword)==$dbpassword) {
        //  ok
    }
    else {
        //  no good
    }
---------------------------------------------------

[[php-5.5-password-functions]]
PHP 5.5 Password Functions
~~~~~~~~~~~~~~~~~~~~~~~~~~

PHP 5.5 adds a number of password functions to simplify password hashing
and checking. Even if you haven’t yet got 5.5 on your server, it is
still helpful to know a little about them, and see how we can use
something similar in our code.

`$hash = password_hash($password,$algorithm[,$options]);`::
	This function takes the password, and hashes it according to the given algorithm code. The algorithm code is an integer, with some pre-defined constants to make them more memorable.
	To be compatible with the method in this discussion, use `PASSWORD_BCRYPT`.
`$result = password_verify($password,$hash);`::
	Returns a boolean indicating whether the password matches the hash. Since the hash includes all of the relevant details, no more information is necessary.

You can implement your own simple versions of the above functions as
follows. This will be compatible to the password hashes above.

[source,php]
------------------------------------------------------------------------
//  See http://php.net/manual/en/function.password-hash.php#113490
 
if(!function_exists('password_hash')) {
    define('PASSWORD_BCRYPT',1);
    define('PASSWORD_DEFAULT',1);
    function password_hash($password,$algorithm=PASSWORD_BCRYPT) {
        $salt = base64_encode(mcrypt_create_iv(22, MCRYPT_DEV_URANDOM));
        $salt = str_replace('+', '.', $salt);
        return crypt($password, '$2y$10$'.$salt.'$');
    }
}
if(!function_exists('password_verify')) {
    function password_verify($password,$hash) {
        return crypt($password,$hash)==$hash;
    }
}
------------------------------------------------------------------------

Further Reading

----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
<li><a href="http://oscarm.org/2012/06/using-bcrypt-to-store-passwords/">Using bcrypt to store passwords | Bytes in the Margin</a></li>
<li><a href="http://alias.io/2010/01/store-passwords-safely-with-php-and-mysql/">alias.io — How to store passwords safely with PHP and MySQL</a></li>
<li><a href="http://stackoverflow.com/questions/401656/secure-hash-and-salt-for-php-passwords#answer-401684">security - Secure hash and salt for PHP passwords - Stack Overflow</a></li>
<li><a href="http://php.net/manual/en/book.password.php">Password Hashing (PHP Documentation)</a></li>
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Links

oscarm.org/2012/06/using-bcrypt-to-store-passwords/

alias.io/2010/01/store-passwords-safely-with-php-and-mysql/

stackoverflow.com/questions/401656/secure-hash-and-salt-for-php-passwords#answer-401684

php.net/manual/en/book.password.php
