Reputation: 28793
I have seen in some MVC applications the use of Token keys to prevent CSRF. A typical example of where it may be used is on the delete method for a post.
And I've seen implementations using both GET and POST methods.
An example GET request link with a token:
https://domain.com/posts/G7j/delete/EOwFwC4TIIydMVUHMXZZdkbUR0cluRSkFzecQy3m5pMTYVXRkcFIBWUZYLNUNSNgQKdnpTWu
And an example of a POST request with a token:
<form action="/posts/G7j/delete" method="post">
<input type="hidden" name="token" value="EOwFwC4TIIydMVUHMXZZdkbUR0cluRSkFzecQy3m5pMTYVXRkcFIBWUZYLNUNSNgQKdnpTWu" />
<button type="submit">Delete</button>
</form>
I've been looking into implementing this into my CakePHP applications based on the documents: http://book.cakephp.org/2.0/en/core-libraries/components/security-component.html
And according to the documents, adding the Security component auto adds the form key to all forms that use the Form Helper.
e.g.
public $components = array(
'Security' => array(
'csrfExpires' => '+1 hour'
)
);
However I have some questions:
1.) Why use POST over GET for some actions such as delete? As the request in the controller will check if the user is authenticated, has permission, and that it has the correct form key.
2.) How can I use the security component with a GET request in CakePHP? Presume I would also need to handle the routing as well.
Upvotes: 1
Views: 8423
Reputation: 28793
johhniedoe pointed me to Croogo 1.3 for an example of how they are have done something similar to what I asked in my question. Because 1.3 was targeted to CakePHP prior to 2.x, the code was a little out of date, so I've amended it as follows:
First create the link (in this case a delete link) with the CSRF Token used by the Security Component passed as a parameter called token
.
<?php echo $this->Html->link('Delete', array('action'=>'delete','token'=>$this->params['_Token']['key'])); ?>
Next I've created a wildcard Route Connection to handle the token (this isn't essential usually, but because we're not using the NAMED Token and I wanted to keep the URL cleaner):
Router::connect('/:controller/:action/:token',
array('controller' => 'action'),
array(
'pass' => array('token')
)
);
And then finally handle it in your method like so:
public function delete(){
if (!isset($this->params['token']) || ($this->params['token'] != $this->params['_Token']['key'])) {
$this->Security->blackHoleCallback = '__blackhole';
} else {
// do delete
}
}
This basically says if the token doesn't match then use the blackhole callback function __blackhole
, which I define in my AppController to show an error.
One last thing to be aware of though is that you must allow the token to be used more than once and last for example an hour, this is because otherwise the token will be reset and no longer match after the GET request is sent.
public $components = array(
'Security' => array(
'csrfExpires' => '+1 hour',
'csrfUseOnce' => false
)
);
Upvotes: 1
Reputation: 221
Firstly CakePHP uses post links to delete as an added level of security because lets say for example your authentication system is not 100% secure and users can access a delete method by manually typing in a URL - if I go in and type /users/delete/10 , I can actually delete users on your server which is risky as you can imagine.
Secondarily GET requests can be cached or bookmarked so users who bookmark these links could end up navigating to broken links which is never a good thing ,also sensitive data will be visible in the URL so for example if someone bookmarks a login page with the GET variables intact - this could compromise there security.
Finally you can easily generate your own tokens using the following code :
$string = Security::hash('string', 'sha1 or md5', true);
print $this->Html->link("Link to somewhere",array("controller"=>"users","action"=>"delete",$string));
The above will use the salt key setup in your core config file but you can also replace true with a custom salt.
Upvotes: 3
Reputation: 2202
For the first question:
The reason probably lies in the definition of HTTP methods. GET
is defined as one of the safe methods, which means that it can not be used for changing the state of the server but only for retrieving information. You can read more on the HTTP methods on this link. Since HTML forms are not capable of sending HTTP DELETE
request the 'workaround' is to use some of the available methods and if you rule out the GET
as a 'safe method' it leaves POST
. You can of course use GET
to delete stuff, many do, but GET
request is by convention expected not to change anything.
edit: If you are interested to read more about HTTP methods and browser/HTML support check out this SO question
Upvotes: 2