<sect1 id="more-secure-passwords-with-php">
  <title>More Secure Passwords with PHP</title>
  <para>
    Posted on Wednesday, 4th September 2013 by admin
  </para>
  <para>
    Update 2: This article has been superceeded by a more to-the-point
    article which focuses more on using the newer functions.
  </para>
  <para>
    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.
  </para>
  <para>
    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.
  </para>
  <para>
    A separate article will discuss protecting your database from SQL
    injection attacks using prepared statements. This article will
    discuss making passwords harder to crack.
  </para>
  <para>
    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.
  </para>
  <sect2 id="for-users">
    <title>For Users</title>
    <para>
      From a user point of view, these are the practices you need to
      follow:
    </para>
    <itemizedlist>
      <listitem>
        <para>
          Have a different password for each online account. Use some
          sort of secure password safe to help keep track of all of your
          passwords.
        </para>
      </listitem>
      <listitem>
        <para>
          Always change your password if you have learned that the
          website has been compromised.
        </para>
      </listitem>
      <listitem>
        <para>
          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.
        </para>
      </listitem>
    </itemizedlist>
    <para>
      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.
    </para>
  </sect2>
  <sect2 id="password-storage">
    <title>Password Storage</title>
    <para>
      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.
    </para>
    <para>
      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.
    </para>
    <para>
      The point to hashing is that the process is
    </para>
    <para>
      There are many ways of hashing strings, but, in general, you need
      a method which:
    </para>
    <itemizedlist>
      <listitem>
        <para>
          can be implemented in PHP
        </para>
      </listitem>
      <listitem>
        <para>
          hasn’t been cracked
        </para>
      </listitem>
      <listitem>
        <para>
          takes long enough to slow down a cracker without blowing all
          of your resources
        </para>
      </listitem>
    </itemizedlist>
    <para>
      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.
    </para>
  </sect2>
  <sect2 id="salts">
    <title>Salts</title>
    <para>
      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.
    </para>
    <para>
      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.
    </para>
    <para>
      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 make things harder, but not impossible.
    </para>
    <para>
      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.
    </para>
  </sect2>
  <sect2 id="user-registration">
    <title>User Registration</title>
    <para>
      Typically a user does the registration, but what follows also
      applies if the registration is performed by the administrator.
    </para>
    <para>
      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.
    </para>
    <para>
      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.
    </para>
    <para>
      The SQL to create such a table would be something like this:
    </para>
    <programlisting language="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
);
</programlisting>
    <para>
      The <literal>passwordhash</literal> field is a character field
      fixed to 40 characters, which the output you get from the
      <literal>sha1()</literal> function.
    </para>
    <para>
      The PHP to add a new user to the table would look as follows:
    </para>
    <programlisting language="php">
$sql='INSERT INTO users(email,password,etc) VALUES(?,?,?)';
$prepared=$pdo-&gt;prepare($sql);
$data=array($email,sha1($password),$etc);
$prepared-&gt;;execute($data);
</programlisting>
    <para>
      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.
    </para>
    <para>
      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.
    </para>
    <para>
      For the table, add a salt field:
    </para>
    <programlisting language="sql">
CREATE TABLE users (
    -- etc
    passwordhash CHAR(40), -- for use with sha1()
    passwordsalt CHAR(20), -- or whatever length
    -- etc
)
</programlisting>
    <para>
      For the PHP, concatenate the salt and add the salt and the hashed
      string to the table.
    </para>
    <programlisting language="php">
$salt='....................';   //  put in some real random text here
$sql='INSERT INTO users(email,password,salt,etc) VALUES(?,?,?)';
$prepared=$pdo-&gt;prepare($sql);
$data=array($email,sha1($salt.$password),$salt,$etc);
$prepared-&gt;execute($data);
</programlisting>
  </sect2>
  <sect2 id="authentication">
    <title>Authentication</title>
    <para>
      The main purpose of passwords is, of course, authentication. That
      is, to allow a user to log in using their user name and password.
    </para>
    <para>
      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:
    </para>
    <programlisting language="php">
$email='…'; //  From login
$password='…';  //  From login
$sql='SELECT count(*) FROM users WHERE email=? AND password=?';
$prepared=$pdo-&gt;prepare($sql);
$data=array($email,sha1($password));
$prepared-&gt;execute($data);
if($prepared-&gt;fetchColumn()) {
    //  successful
}
else {
    //  no good
}
</programlisting>
    <para>
      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.
    </para>
    <para>
      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:
    </para>
    <programlisting language="php">
$email='…'; //  From login
$password='…';  //  From login
//  get salt
    $sql='SELECT salt FROM users WHERE email=?';
    $prepared=$pdo-&gt;prepare($sql);
    $data=array($email);
    $prepared-&gt;execute($data);
    $salt=$prepared-&gt;fetchColumn();
//  salt &amp;amp; check password
    $sql='SELECT count(*) FROM users WHERE email=? AND password=?';
    $prepared=$pdo-&gt;prepare($sql);
    $data=array($email,sha1($salt.$password));
    $prepared-&gt;execute($data);
if($prepared-&gt;fetchColumn()) {
    //  successful
}
else {
    //  no good
}
</programlisting>
    <para>
      It might be possible to simplify the query query to a single
      SELECT statement by relying on the database’s own
      <literal>sha1()</literal> function, but that is less flexible. It
      might look like this:
    </para>
    <programlisting language="sql">
SELECT count(*) FROM users WHERE email=? AND password=sha1(concat(salt,?))
</programlisting>
  </sect2>
  <sect2 id="using-php-crypt">
    <title>Using PHP <literal>crypt()</literal></title>
    <para>
      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.
    </para>
    <para>
      The <literal>crypt()</literal> 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.
    </para>
    <para>
      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.
    </para>
    <para>
      In PHP, the <literal>crypt()</literal> function requires the
      password, of course, and possibly a salt, which is the whole point
      to this exercise.
    </para>
    <para>
      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.
    </para>
    <para>
      To add a password to the database:
    </para>
    <itemizedlist>
      <listitem>
        <para>
          Generate a salt
        </para>
      </listitem>
      <listitem>
        <para>
          Use <literal>crypt()</literal> to generate the hash
        </para>
      </listitem>
      <listitem>
        <para>
          Add the result to the database
        </para>
      </listitem>
    </itemizedlist>
    <programlisting language="php">
//  Get the user’s password
//  Create Initial Salt
    $salt='';
    $chars='./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
    for($i=0;$i&amp;lt;22;$i++) $salt.=$chars[rand(0,63)];
    $salt=sprintf('$2a$10$%s',$salt);   //  preferably '$2y$10$…' if PHP &amp;gt;= 5.3.7
//  Generate the Hash
    $dbpassword=crypt($password,$salt);
//  Add to the database …
</programlisting>
    <para>
      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:
    </para>
    <programlisting language="php">
crypt($password,$dbpassword)==$dbpassword
</programlisting>
    <para>
      This gives us the following test:
    </para>
    <programlisting language="php">
//  Get password from database
//  Test by re-hashing:
    if(crypt($password,$dbpassword)==$dbpassword) {
        //  ok
    }
    else {
        //  no good
    }
</programlisting>
  </sect2>
  <sect2 id="php-5.5-password-functions">
    <title>PHP 5.5 Password Functions</title>
    <para>
      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.
    </para>
    <dl>
        <dt>
    <literal>$hash = password_hash($password,$algorithm[,$options]);</literal>
    </dt>
        <dd>
    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.
    </dd>
        <dd>
    To be compatible with the method in this discussion, use
    <literal>PASSWORD_BCRYPT</literal>.
    </dd>
        <dt>
    <literal>$result = password_verify($password,$hash);</literal>
    </dt>
        <dd>
    Returns a boolean indicating whether the password matches the hash.
    Since the hash includes all of the relevant details, no more
    information is necessary.
    </dd>
    </dl>
    <para>
      You can implement your own simple versions of the above functions
      as follows. This will be compatible to the password hashes above.
    </para>
    <programlisting language="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;
    }
}
</programlisting>
    <h2>
    Further Reading
    </h2>
    <ul>
        <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>
    </ul>

    <h2>
    Links
    </h2>
    <ul>
    <li>
    <a href="http://oscarm.org/2012/06/using-bcrypt-to-store-passwords/">oscarm.org/2012/06/using-bcrypt-to-store-passwords/</a>
    </li>
    <li>
    <a href="http://alias.io/2010/01/store-passwords-safely-with-php-and-mysql/">alias.io/2010/01/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">stackoverflow.com/questions/401656/secure-hash-and-salt-for-php-passwords#answer-401684</a>
    </li>
    <li>
    <a href="http://php.net/manual/en/book.password.php">php.net/manual/en/book.password.php</a>
    </li>
    </ul>
  </sect2>
</sect1>