PHP Password Security

If you build websites that require users to register it’s your responsibility to keep their passwords safe. And if you’re storing the passwords in plain text then you’re not doing your job properly. It may be that, like Reddit, you think that storing passwords in plain text leads to a better user experience. I happen to agree with you. But then, like Reddit, what happens if your database is stolen? It’s not just your site that is compromised. Since most users use the same password on multiple sites, all those sites have also been compromised.

No data is entirely secure, and if anyone else has access to your webserver (the company managing the server for you?) or your database (the company storing the backups?) then you don’t have total control over the security anyway. So there’s always a chance your database could be stolen. So, the simple rule is to hash your passwords.

Hashing

A hash is a string derived from the original password via a one-way algorithm. In other words, it’s easy to create the hash from the original, but harder (when used for security, ideally impossible) to create the original from the hash. You store the hash in the database, and when the user signs-in you hash the password they sign-in with and compare it to the hash in the database. Something like this

if( $user->passwordhash == sha1( $_POST['password'] ) )

That way, you never store the user’s password.

There are a number of hashing algorithms in PHP, of which md5 and sha1 are the most commonly used. Unfortunately, neither is as secure as they were once thought to be. It would be better to use a more secure hash, and if you have the Hash engine in your PHP installation (included by default since PHP 5.1.2) then you have access to many more algorithms. So a better example would be

if( $user->passwordhash == hash( 'whirlpool', $_POST['password'] ) )

Rainbow tables

But there’s another problem. Once your database is stolen, the thief has plenty of time to crack the passwords using a simple Rainbow Table attack. This involves creating a large selection of hashes based on likely passwords (e.g. every word in the dictionary) and then comparing the hashes with the hashes in your database. Within an hour or so, half the passwords in your database will probably have been cracked.

To prevent this you should salt each password by adding a random string to it (called a salt or nonce). The time consuming part of a rainbow table attack is building the dictionary of hashes. Adding a random salt to the password means the thief has to build a whole new dictionary of hashes for each salt, making a rainbow table attack too time consuming to be viable. Each password should have a different salt, and the salt doesn’t even need to be secret.

The Code bit

So, for secure passwords you need code that looks something like this

// get a new salt - 8 hexadecimal characters long
// current PHP installations should not exceed 8 characters
// on dechex( mt_rand() )
// but we future proof it anyway with substr()
function getPasswordSalt()
{
    return substr( str_pad( dechex( mt_rand() ), 8, '0',
                                           STR_PAD_LEFT ), -8 );
}

// calculate the hash from a salt and a password
function getPasswordHash( $salt, $password )
{
    return $salt . ( hash( 'whirlpool', $salt . $password ) );
}

// compare a password to a hash
function comparePassword( $password, $hash )
{
    $salt = substr( $hash, 0, 8 );
    return $hash == getPasswordHash( $salt, $password );
}

// get a new hash for a password
$hash = getPasswordHash( getPasswordSalt(), $password );

You don’t have to attach the salt to the hash, you can instead store them separately within the database, but I like keeping them together in a single string. Equally, the salt needn’t be in hexadecimal, but I like the symmetry with the hexadecimal hash.

Finally, as Thomas Ptacek points out, you don’t want the fastest hash algorithm in the world for this – a fast algorithm is more useful to an attacker than it is to you.

New Wishlist site

I’ve been posting a lot about the Zend Framework recently. That’s because I’ve been using it in building a new version of my wish list site. The site is called One Wishlist and is at onewishlist.net. It uses version 0.7 of the Zend Framework. I’ll try moving it to version 0.8 soon.

I’ve made a few changes to the site since the previous version, including substantial design changes, but mostly during the move to the Zend Framework I have added hooks for new features that are planned but not yet implemented. Many of these are the result of feedback from the previous site, so thanks to everyone who made suggestions and comments, the features are on their way.

Meanwhile, expect more Flash posts in the next couple of weeks as I have a new flash game to develop for a client.

Managing 404 errors in the Zend Framework

This applies to version 0.7 of the Zend Framework. Changes for later versions of the framework are at the end of the article.

Early versions of the Zend Framework had a noRoute action that was called when the correct action couldn’t be found. This was a way to deal with some page not found errors. At some point it was dropped – I don’t know when or why because I only started using the Zend Framework recently. It’s still possible to handle non-existent actions using the __call() method of the controller class. But there’s no obvious way to deal with all page not found errors in one place, including instances where the controller doesn’t exist.

The Zend Framework is based around controllers and actions, using URL’s of the form http://www.example.com/controller/action. If no action is specified the index action is used, and if no controller is specified the index controller is used. You can modify the mapping of URLs to controllers and actions by setting up different rewrite routers in the front controller.

A 404 error should occur when the controller or action specified in the URL aren’t defined. But instead of a 404 error, the Zend Framework throws an exception because the controller class or the action method can’t be found. There’s nothing worng with this – it leaves it for each developer to decide what to do. So what should we do?

To generate an appropriate 404 error with a custom page we need either to intercept the request before the error occurs or to catch the exception after the error occurs, and in either case to redirect to an appropriate controller and action.

I created a controller plug-in to do just that. In the pre-despatch method it checks the controller class can be loaded and that it contains the required action. If either is not the case it redirects the request to the index action of the noroute controller. Then one just creates an appropriate noroute controller to display the page not found error.

Here’s the plug-in code.

require_once 'Zend/Controller/Plugin/Abstract.php';

class Bigroom_Controller_Plugin_Noroute
                           extends Zend_Controller_Plugin_Abstract
{
    public function preDispatch(
                       Zend_Controller_Request_Abstract $request )
    {
        $dispatcher = Zend_Controller_Front::getInstance()
                                                ->getDispatcher();
        
        $controllerName = $request->getControllerName();
        if (empty($controllerName)) {
            $controllerName = $dispatcher->getDefaultController();
        }
        $className = $dispatcher
                          ->formatControllerName($controllerName);
        if ($className)
        {
            try
            {
                // if this fails, an exception will be thrown and
                // caught below, indicating that the class can't
                // be loaded.
                Zend::loadClass($className,
                           $dispatcher->getControllerDirectory());
                $actionName = $request->getActionName();
                if (empty($actionName)) {
                    $actionName = $dispatcher->getDefaultAction();
                }
                $methodName = $dispatcher
                                  ->formatActionName($actionName);
                
                $class = new ReflectionClass( $className );
                if( $class->hasMethod( $methodName ) )
                {
                    // all is well - exit now
                    return;
                }
            }
            catch (Zend_Exception $e)
            {
                // Couldn't load the class. No need to act yet,
                // just catch the exception and fall out of the
                // if
            }
        }
    
        // we only arrive here if can't find controller or action
        $request->setControllerName( 'noroute' );
        $request->setActionName( 'index' );
        $request->setDispatched( false );
    }
}

Simply register this plug-in with the front controller and set-up the NorouteController class to display the error page. And remember to send a 404 error header from the noroute controller.

The plug-in doesn’t deal with modules – mainly because I don’t use modules and couldn’t decide whether the NorouteController class should be global or per-module. It shouldn’t be too hard to add module handling to the code.

Zend Framework 0.9

For version 0.9 of the framework you need to change Zend::loadClass to Zend_Loader::loadClass. With this change the plug-in works again as intended.

Zend_View helpers in include path

N.B. This article applies to version 0.7 of the Zend Framework. The first solution suggested below is now incorporated in version 0.8 of the framework.

The Problem

I’m using the Zend Framework at the moment and came across a problem with creating custom helpers for the Zend_View class. All is fine if you place your helpers either within the zend framework’s own helpers folder (not a good idea) or within a directory in your site structure. The problem arises if you place the helpers in a folder in the PHP include path and don’t know the full path to the folder.

Why would that happen? Well, here’s my set-up – on each server I have a folder that is added to the include path. This folder has the same structure on all my servers – development, testing and production – but may be in a different location on some of them. That doesn’t matter because I can add the correct folder to PHP’s include path in the php.ini configuration file on each server.

In the include folder I place a number of common files, including the Zend framework itself and my custom files for use with the framework. To keep things simple I duplicate the structure of the Zend Framework library files within my own custom files, as recommended in the Zend Framework documentation. My own files are placed within a fiolder called Bigroom, just as the Zend files are placed within a folder called Zend.

So, my Zend_View helpers are in the folder at Bigroom/View/Helper within the include path. The helpers are all named accordingly too, e.g. Bigroom_View_Helper_SelectCountry for a select box listing all countries. So far so good and in keeping with the recommendations in the framework documentation.

However, to add my helpers to the set of helpers in the Zend Framework, I have to know the full path to the helpers, not the path relative to the PHP include path, and not just the class name. This is not a bug, it’s how the helpers part of the framework is designed. It’s just a bit odd when compared to the rest of the framework. The code to add the helper directory to the helpers might look like this on my production and test servers -

$view = new Zend_View();
$view->addHelperPath( '/home/web/phpincludes/Bigroom/View/Helper',
                      'Bigroom_View_Helper' );

But on my development system, which is windows based, it would look like this -

$view = new Zend_View();
$view->addHelperPath( 'C:\php\includes\Bigroom\View\Helper',
                      'Bigroom_View_Helper' );

This seems to break with the normal behaviour of the Zend Framework, in which classes are automatically found from within the include path based on their class name. It also makes it difficult to create portable code since one must ensure that the include path is exactly the same on all systems using the code.

I have two solutions to this. The first involves altering the code in the Zend Framework, while the second, my preferred option, does not.

Solution 1

In the first solution, we make two small modifications to the Zend_View_Abstract class.

First, in the _loadClass method replace the line

} elseif (is_readable($dir . $file)) {

with

} elseif (Zend::_isReadable($dir . $file)) {

If you do all your work on the same platform, this will suffice. But for cross platform use we also need to check that the directory separators are the same. So the second change is in method _addPath where you need to add the statement

$dir = str_replace( array('/', '\\'), DIRECTORY_SEPARATOR, $dir );

after

$dir = rtrim($dir, '\\/' . DIRECTORY_SEPARATOR) 
                . DIRECTORY_SEPARATOR;

Now you can add helper paths using a path relative to the include path and with either / or \ as the directory separator. So portable code to add my helpers becomes

$view = new Zend_View();
$view->addHelperPath( 'Bigroom/View/Helper',
                      'Bigroom_View_Helper' );

There are, however, two drawbacks to this approach. First it involves modifying the Zend framework itself. That’s not a good idea since you have to remember to redo the modifications when upgrading to a new version of the framework. And secondly, Zend::isReadable is significantly slower than is_readable, and here it’s being used every time you use a helper. The speed hit probably won’t be significant but it would be nice to avoid it.

Solution 2

My second solution to the problem is to extent the Zend_View class with my own Bigroom_View class that implements a new function for adding helper paths based on the class prefix only and looks for the files within the include path. This is in keeping with the normal operation of the Zend Framework where a class name is all that’s needed to find that class within the include paths.

Here’s the code

require_once 'Zend/View.php';

class Bigroom_View extends Zend_View
{
    public function addHelperPrefix( $classPrefix )
    {
        $path = DIRECTORY_SEPARATOR
              . str_replace('_', DIRECTORY_SEPARATOR, $classPrefix);
        $dirs = explode( PATH_SEPARATOR, get_include_path() );
        foreach( $dirs as $dir )
        {
            $fullpath = $dir . $path;
            if( is_readable( $fullpath ) )
            {
                $this->addHelperPath( $fullpath, $classPrefix );
            }
        }
    }
}

Now I use Bigroom_View instead of Zend_View and add helpers as follows.

$view = new Bigroom_View();
$view->addHelperPrefix( 'Bigroom_View_Helper' );

Which seems a lot neater to me.

Why I like the Zend Framework

I’ve been researching the Zend Framework for PHP in relation to a project I’m working on and I have to say I’m now a fan of it. There’s lots to like about it, and some to dislike about it too. But to me the best aspect of it is the flexibility it offers. Unlike some frameworks, you don’t have to use the complete package. Instead you can pick and choose which bits to use and plug it together with your own classes to get a complete product that meets your particular needs.

For myself, I’m using the controller and view aspects of the framework with a few minor additions of my own. Alongside this I’m using the model set-up that I’ve evolved over a number of projects, with the addition of a few components from the Zend_Framework. Slotting the framework into my current workflow has proven remarkably easy.

If I was to make one complaint it would be about the documentation. It has some gaps in it at the moment but it’s early days yet. Much of the detail I’ve discovered has been by inspecting the source code, but the code itself is clearly structured and well commented so it’s relatively easy to rummage through and find what you’re looking for.

If you’re looking for a PHP framework it’s worth checking out more than one since they all have different advantages and disadvantages. Here’s a short list to consider.

PHP mail() function and GMail

or Another reason to avoid PHP’s mail() function

Since launching Giefan.com on Monday we’ve fixed a few bugs that only came to light as a result of the way other people work. One such was an email problem with messages sent to GMail (thanks to Alan for catching this). I’ve never had a GMail account so it wasn’t one of the email applications we tested the messages with.

Email messages were arriving at GMail with messages garbled into a string of apparently random characters and with the sender listed as unknown. I was using mb_send_mail() to send UTF-8 email messages from PHP. I always intended to implement a different email solution at some point but evaluating the various possibilities and making a choice didn’t seem as important for the launch period as many of the other features so I chose to start with mb_send_mail() and switch to another solution later.

mb_send_mail() was sending messages as UTF-8 and base64 encoded, and it seems that GMail failed to correctly understand this from the email headers. As a result, GMail simply displayed the base64 encoded data. The quick fix just to get it working was to switch to PHP’s mail() function. This would suffice for a few hours while we sought a better solution.

The other problem was GMail displaying the sender as unknown, despite having the sender data in the From: header. I tried various different formats for the From: header (quotes around the name, extra spaces etc.) but none seemed to work for GMail. Instead, it always displayed the From: header line in the body of the message (there were no blank lines in the header so that’s not the problem, and the messages worked with a number of other email applications we tested them with).

I still haven’t discovered what the problem was but have three possibilities.

  1. Does GMail dislike the unconventional (but not technically wrong) ordering of the headers that mail() and mb_send_mail() produce?
  2. Does GMail distrust messages in which the X-Mailer header lists “PHP mailer” as the system used and consequently displays the sender as unknown and places the from data in the message?
  3. Is mail() doing something wrong in its headers, and most email applications are simply ignoring the mistake and figuring out what’s intended?

If anyone knows the actual cause please add a comment below.

Anyway, the obvious solution was to avoid PHP’s mail functions altogether so I hurriedly added Chris Corbyn’s Swift Mailer script and altered my code to use it, and all was well. I haven’t thoroughly tested Swift Mailer yet but it seems to be working fine – the messages are in UTF-8 and GMail is happy with them.

Swift Mailer is just one of many alternatives to the mail() function but I like it for it’s simplicity of use. Hopefully, when I test it out more thoroughly later this month I’ll still be very happy with it.

PHP 5.2 – Nesting level too deep – recursive dependency?

I installed PHP 5.2 on one of my testing servers today and a couple of bits of code that previously worked fine in version 5.1.6 threw fatal errors in the new version. The error message was “Nesting level too deep – recursive dependency?” and it took a little time to track down the root of the problem. Here’s what I’d done wrong.

In PHP there are two comparison operators, == and ===. It’s generally known that the first is not strict about type but the second is. So, for example

echo ( false == 0 ); // true
echo ( false === 0 ); // false
    - 0 is an integer and false is a boolean

My problem arose from using non-strict typing with objects.

$a = new MyObj();
$b = new MyObj();
if( $a == $b )
...

I hadn’t considered what I was doing with this code. When comparing two objects using the non-strict comparison operator (==) PHP compares all the properties of the objects and if they match the objects are deemed to be equal. If they don’t match they are not equal. In effect, we have a recursive comparison of all the properties of each object, and all their properties, etc. until we reach basic data types like strings and integers.

If, however, we use strict comparison (===), PHP will check whether the two objects are exactly the same object, not just objects with the same properties.

class MyObj
{
    public $p;
}

$a = new MyObj();
$b = new MyObj();
$c = new MyObj();
$a->p = 1;
$b->p = 1;
$c->p = 2;
echo ( $a == $c ); // false
echo ( $a == $b ); // true
echo ( $a === $b ); // false

The problem arises if you have circular references in your objects properties. So, for example

class MyObj
{
    public $p;
}
class OtherObj
{
    public $q;
}

$a = new MyObj();
$b = new OtherObj();
$a->p = $b;
$b->q = $a; // the circular reference: $a->p->q === $a

$c = new MyObj();
$d = new OtherObj();
$c->p = $d;
$d->q = $c;// another circular reference: $c->p->q === $c

echo ( $a == $c ); // Fatal error:
    Nesting level too deep - recursive dependency?

In order to compare $a to $c, PHP must compare their properties. So the logic in PHP goes something like this: $a == $c if $a->p == $c->p if $a->p->q == $c->p->q if $a->p->q->p == $c->p->q->p etc. indefinitely.

PHP 5.1 seemed to smooth over the problem somehow (probably after a certain level of recursion it simply returned false) – and usually it worked out fine. PHP 5.2 correctly produces the fatal error above.

Once you know the problem, the solution is easy – use strict comparison.

echo ( $a === $c ); // false (and no error)

The strict comparison will simply check whether the two objects are at the same location in memory and so doesn’t even look at the values of the properties.

N.B. The same problem can arise when using the negated comparison operators (use !== instead of !=) and when using in_array (use in_array‘s third parameter to indicate strict comparison).