Securing Cakephp Forms including Ajax, JQuery & Security
While building a recent CakePHP solution in addition to securing my forms I needed to use some JQuery-driven Ajax calls. Although on the surface this appeared straight forward the documentation was lacking in the detail required. I wasted alot of time trying different things that didn’t work idependantly but once put together provided what I believe is a working solution. It may need some refinement over time but hopefully this will save others wasting so much time in code-wilderness.
In this post I will show the complete process of securing both user and ajax submitted forms in CakePhp 2.0. For the purposes of the demonstration, and not that they were needed at all, I have used the first steps of the introduction blog tutorial. I used the Posts
as my only database table and baked up a Model, Controller and Views quickly using the console. If you are interested in the setup then dropdown to the section labelled setup for an explanation.
Step 1) Installing the CakePHP Security Component:
Add the Security component to your AppController. Security can protect your form in a number of ways including CSRF which is what I was most interested in.
class AppController extends Controller {
public $components = array('Security');
public $helpers = array('Html', 'Form', 'Session');
function beforeFilter() { }
}
Line 4 is the most important line here as we are actually including the Security component. The other remaining lines are just defaults for a AppController. You will notice the beforeFilter()
function. You can add $this->Security->blackHoleCallback = 'blackhole';
to this function and then create a new function called blackhole
that handles your errors. This is worthwhile as you want to give away minimal information in case someone is trying to be naughty. If Security is working successfully then you should see some added code at the bottom of all your forms as shown here in the green box for an Add Post Form.
Step 2) Creating an Ajax Form and submitting it with JQuery:
Getting a working Ajax Submission using JQuery is pretty simple but getting it to work now we’ve enabled the Security component is where I had most difficulty mainly due to poor documentation. What I needed to do from the beginining was to create a hidden form that I could serialize and not to push variables straight into a string e.g. line 5 – in other words the following did NOT work:
It appeared that what was needed was a form hidden from users that could be submitted by JQuery. For the purposes of this demonstration I have created this inside a view called ajaxdemo and added public function ajaxdemo() {}
to my PostsController
. You will note in this form I have not used $this->Form->hidden
so you can see the fields. Also note in this example snippet I have two divs: one I use to trigger the Ajax submission; and another that shows some results.
AJAX TEST
$lt;?php
echo $this--->Form->create(array('action' => 'tracker', 'id' => 'ajax_form'));
echo $this->Form->input('var_one', array('value' => '11'));
echo $this->Form->input('var_two', array('value' => '22'));
echo $this->Form->input('var_three', array('value' => '33'));
echo $this->Form->end();
?>
Add at the bottom of the page we can add the required javascript.
Step 3) Making it all work:
My recommedation at this point is to go back to your AppController
and comment out the public $components = array('Security');
line so you can test your Ajax call without the interferrence of Security. However, I expect you, like me, want to just crack on with the business at hand so here goes.
If we try and make a Ajax call now should get binned by the Security component. However we can add a beforeFilter
exclude in the form shown:
function beforeFilter() {
parent::beforeFilter();
if ($this->action == 'tracker') {
$this->Security->validatePost = false;
}
}
And update our tracker action to the following noting how we prevent a view error using the $this->autoRender = false;
and check that we have a Ajax request.
public function tracker() {
$this->autoRender = false;
if($this->RequestHandler->isAjax()) {
$output['temp_one'] = $this->data['Post']['var_one'] * $this->data['Post']['var_two'];
$output['temp_two'] = $this->data['Post']['var_two'] * $this->data['Post']['var_three'];
echo json_encode($output);
}
}
Don’t forget to add var $components = array('RequestHandler');
to the top of our PostsComponent
just under the class declaration.
You should now be able to test this and see the results of our simple sums added to the ajax_result div.
Other Considerations
1) Data Validation
Providing you have set up a model for this controller then it is easy to ensure that everything is valid. The model should contain all of the validation rules you wish to apply to the data in the model. Providing you call the $this->Controller->save() function then the data is validated.
3) Data Sanitization
Check out the Sanitize Library.
4) Use NONCE for Security
If you are using the Security component you should find that the CSRF feature automatically expires after a settime thus rendering forms non-submitable after a period of time.
Set Up
This is how I set up the system for this demonstration. I’m assuming you have downloaded and set up a basic working CakePHP installation.
Step 1) In phpmyadmin (or similar database management tool) run the following SQL taken from the CakePHP Blog Tutorial for Version 2.
/* First, create our posts table: */
CREATE TABLE posts (
id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(50),
body TEXT,
created DATETIME DEFAULT NULL,
modified DATETIME DEFAULT NULL
);
/* Then insert some posts for testing: */
INSERT INTO posts (title,body,created)
VALUES ('The title', 'This is the post body.', NOW());
INSERT INTO posts (title,body,created)
VALUES ('A title once again', 'And the post body follows.', NOW());
INSERT INTO posts (title,body,created)
VALUES ('Title strikes back', 'This is really exciting! Not.', NOW());
Step 2) In your command line run the following commands just accepting the default everytime except when it asks if you want to create some basic class methods.
cd ~/cakephp/app
../lib/Cake/Console/cake bake
M
1
....
C
1
...
V
1
...
You should now have worked through baking a Model, Controller (not forgetting the basic class methods) and some views. Check it works by going to http://yourdomain/posts
Hello, good post.
You can still make secure ajax calls using Cake’s provided form security mechanics.
To do this, render a non-visible form and place inputs to store the ajax call parameters. Then, with Javascript set these parameters in your form and do the ajax call by serializing it. Remember that if you have CSRF check enabled (and one-token-per-session is disabled) you will have to update the form with a new valid CSRF token (you can read it in the controller with `$this->request->params[‘_Token’][‘key’]`).
Example:
Form->create(‘AjaxForm’);
echo $this->Form->hidden(‘value’);
echo $this->Form->end();
?>
function makeAjaxCall() {
$.post(
ajaxUrl,
$(‘#AjaxForm’).serialize(),
function(data) {
$(‘#AjaxForm [name=”data[_Token][key]”]’).val(data.newCsrfToken)
}
);
};
**For further reference, we have created a component that allows to maintain security enabled on client side forms that are dinamically modified, and removes the need to unlock fields or actions when making ajax calls. You can find it at https://github.com/QTSdev/DynamicSecurity.**