nette/nette — pull request #1385 by fabik (2014-01-30 00:29:36+00:00)
Title: Application: added CSRF protection for signals [Closes #469]

The [RFC 2616, section 9.1.1](http://tools.ietf.org/search/rfc2616#section-9.1.1) states:

> In particular, the convention has been established that the GET and HEAD methods SHOULD NOT have the significance of taking an action other than retrieval. These methods ought to be considered "safe". This allows user agents to represent other methods, such as POST, PUT and DELETE, in a special way, so that the user is made aware of the fact that a possibly unsafe action is being requested.

So we should use POST for secured signals (and ideally for all signals with side effects).

We can do this using a JavaScript, that sends links with parameter `data-nette-post` using POST. We can also easily create a fallback method for users without JavaScript - if an user attempts to access a protected page using GET method, we will show him a confirmation form which will execute the request with the CSRF token in POST param.

This commit allows you to protect signal by calling e.g. `$this->addProtection("Do you really want to delete user '$username'?");` at the beggining of the signal. After that, if you access the page via GET or via POST but with an invalid token, a confirmation dialog will be displayed.

A secured link can be created using ``, which adds the parameter `data-nette-post`. You can send links with this parameter by method POST using the following JavaScript:

jQuery(function($) {
$(document).on("click", "a[data-nette-post]", function(event) {
var form = document.createElement("form");
form.method = "POST";
form.action = this.href;
form.style.display = "none";
var post = $(this).data("nette-post");
for (var key in post) {
var input = document.createElement("input");
input.setAttribute("type", "hidden");
input.setAttribute("name", key);
input.setAttribute("value", post[key]);
form.appendChild(input);
}
$(form).submit();
event.preventDefault();
});
});

(Maybe we should also create some official JavaScript like netteForms.js.)

If you want to send signals using AJAX, you have to just modify your JavaScript, so that it will use method POST and send POST params contained in `$control->getCsrfPost()`.

The confirmation dialog can be customized by overriding the method `displayCsrfConfirmationDialog()` in Presenter.