How to make use POST method with CSRF token for simple actions like deleting a comment, or a simple-click action.
Sometimes, it may be hard to secure some actions on website. Here are some few examples where creating a form is clearly overkill. Those simple actions should be secured.
- disable, validate, delete a comment
- enable an account
- logout
- send e-mail
symfony 1.4 legacy
In symfony 1.4, it was possible to secure a link with a POST method, using helper link_to
. This helper was used like this:
echo link_to('@route', array('sf_method' => 'POST'), 'your text);
Easy, isn't it? This helper created a Javascript function to post form:
<a
href="/your-route"
onclick="var f = document.createElement('form'); f.style.display = 'none'; this.parentNode.appendChild(f); f.method = 'post'; f.action = this.href"
>your text</a
>
See code on github for more details : https://github.com/symfony/symfony1/blob/1.4/lib/helper/UrlHelper.php#L610
POST a link with jQuery
Add this snippet to your javascript library. Make it included on page rendering:
/**
* Sample usage:
*
* <a href="/blog/48/delete" data-method="POST">delete this post</a>
*/
$(document).ready(function () {
// Every link with an attribute data-method
$("a[data-method]").click(function (event) {
event.preventDefault();
var target = $(event.currentTarget);
var method = target.attr("data-method");
var action = target.attr("href");
// Create a form on click
var form = $("<form/>", {
style: "display:none;",
method: method,
action: action,
});
form.appendTo(target);
// Submit the form
form.submit();
});
});
And in your template:
<a
data-method="POST"
href="{{ path('message_delete', {id: message.id, token: token}) }}"
>delete</a
>
Simple CSRF in Symfony2
Finally, we need to pass a token
value to the template and check it:
// src/Acme/DemoBundle/Controller/MessageController.php
public function listAction()
{
$token = $this->get('form.csrf_provider')->generateCsrfToken('message_list');
return $this->render('AcmeDemoBundle:Message:list.html.twig', array(
'token' => $token,
'messages' => array() // put your business here
))
}
public function deleteAction()
{
if (!$this->get('form.csrf_provider')->isCsrfTokenValid($intention, $token)) {
$this->get('session')->setFlash('notice', 'Woops! Token invalid!');
return $this->redirect('message_list');
}
return $this->render('AcmeDemoBundle:Message:list.html.twig', array(
'token' => $token,
'messages' => array() // put your business here
))
}
Constraint routing
If you don't change your routing, this deleteAction
method will allow POST and GET requests. You need to change your routing and add a requirement on method:
# routing.yml
message_delete:
pattern: /message/{id}/delete
requirements: { _method: POST }