Make your CakePHP forms a lot more secure

Update: 11/06/2008
Tarique Sani pointed out that I had an extra line of code, which wasn’t necessary to make all this work (perhaps an old habit, but the post has been modified to reflect the change).
———————

My recent post started up some good conversations and I figured that a good follow-up would be an example of how to use the Security component to make your forms much more… secure.

We’ll start with a basic usage and then expand a little…

First let’s assume a basic model:

class User extends AppModel {

  var $name = 'User';

  var $validate = array(
      'name'=>array('rule'=>'notEmpty'),
      'email'=>array('rule'=>'email'),
      'password'=>array(
              'Cannot be empty' => array('rule'=>'notEmpty'),
            'Must be at least 4 chars' => array('rule'=>array('minLength', 4))
      )
  );
}

We’ve got some basic validation rules defined (and note, there is no ‘required’=>true, which was the point of confusion and problems for some people).

Now we’ll assume that we have an evil user, who wants to tamper with our form fields and bypass the ‘name’ field, for example, to save blank data into the DB. If we leave our form unsecured, it is very easy to remove the field from the data array (by using some basic hacking tool) and since it won’t be present in the array, the validation will be skipped and the form data will be saved with a blank name.

So how can we avoid this problem, by using the Security component?

Let’s build our controller:

class UsersController extends AppController {

    var $name = 'Users';
    var $components = array('Security');

    function add() {
        if(!empty($this->data)) {
            $this->User->save($this->data);
        }
    }
}

And a basic view for the add action:

echo $form->create();
echo $form->inputs(array('name', 'email', 'password'));
echo $form->end('Register');

Pretty simple, right?
We’ve made our form quite secure with only one “extra” line of code:
var $components = array(‘Security’);

Let me explain exactly what happens behind the scenes…

The Security component will create a hash based on the form fields produced by our Form Helper. If someone tampers with the form fields (by adding or removing or changing any field), the hash is not going to match with the expected one and the add() action will fail.

Yep, it’s that simple. You are welcome to try to mess around with the form by using your favorite POST-modifier tool (maybe: https://addons.mozilla.org/en-US/firefox/addon/1290, thanks to Jonah for providing the link).

You’ll notice that the action will fail without any error message (you’ll just get a blank screen in most cases). In my opinion, that’s just fine for any evil user… why make the app user-friendly for them?

Well, let’s be nice and make it a bit more user-friendly. It’s a good exercise, if anything…

First, we’ll extend our controller a little by creating a beforeFilter() method, let’s add one more line of code to it:

  function beforeFilter() {
        $this->Security->blackHoleCallback = 'fail';
  }

The $this->Security->blackHoleCallback = ‘fail’; tells the Security component to call our custom fail() method, in case the action gets black-holed, which it will if someone tampers with the form.

So let’s create the fail() method:

 function fail() {
        $this->cakeError('youSuck');
    }

Alright, as you can see this method will in turn trigger the youSuck() error method, which we’ll need to build in our custom app_error.php handler:

So, if you don’t have one, create app_error.php in your /app/ root directory (this is where you define custom error methods, or override existing ones).

class AppError extends ErrorHandler {
    function youSuck() {
        $this->controller->set(array(
             'name' => __('You are an evil person', true)
         ));

        $this->__outputMessage('bad_user');
    }
}

And now we need a new view bad_user.ctp, which is placed in /app/views/errors/bad_user.ctp

<h2><?php echo $name; ?></h2>
<p class="error">
  <strong><?php __('Error'); ?>: </strong>
  <?php  __('Do not try that again!'); ?>
</p>

Now, when someone tampers with the form, they’ll get a nice error page suggesting what we really think of hackers.

Hopefully you can see how Security component can make your app a lot more secure with just 2-3 extra lines of code. And at the same time we’ve covered a little example of how to best handle custom errors.

P.S. You can make your form even more secure by supplying additional params to save().

%d bloggers like this: