CSRF attacks are among the most common security threats that affect web applications. The details of how CSRF attacks work have been covered extensively around the web. If you want to learn more, I strongly recommend reading Chris Shiflett’s popular post as well as the OWASP wiki page on these attacks.
The most common way to combat CSRF attacks is known as the “Synchronizer Token Pattern” (I’ll go into this a bit more later). Lithium provides helpers for this pattern with its RequestToken and Security view helper classes.
In most instances, token validation is done in controllers for actions that require it. However, being a bit lazy, I wanted an application wide means of validating these tokens before even getting to the controller. To put this in place, you first have to enable sessions in your application. To do this, simply uncomment
require __DIR__ . '/bootstrap/session.php'; in
app/config/bootstrap.php. In the “Synchronizer Token Pattern”, the tokens that get submitted from forms or AJAX requests gets validated against a token hash that is stored in a user’s session.
The next order of business is to extend Lithium’s Security view helper. This can can go in the
app/extensions/helper directory. By default, it will only render an input tag with a CSRF token. Because I want to validate AJAX requests as well, I need it to render both input tags and meta tags.
With the extended Security helper, my layout and views can now be modified to send the CSRF tokens. For AJAX requests, the token is read from a meta tag on the page.
The tokens can now be added to your page like so…
Lastly, the handling for incoming tokens and validation. This filter can be placed in
The filter first checks the request method to see if token validation is required. Safe request methods such as GET do not require token validation. As a matter of good security practice, requests made using methods like GET or HEAD should NOT perform any potentially desctructive operations within your application. eg: A link to http://example.com/user/delete/16 should not actually delete user 16.
If the request requires token validation, the token is first pulled from the request headers. If a token comes in the request body, that token overrides the one from the header. If the token is invalid, the request is then redirected to its referrer. In this filter, a new token hash is generated for all requests aside from AJAX requests. AJAX requests are excluded to simplify the process of updating a token once it’s been used.
The above filter may not meet all your needs. Depending on the application, it may be required to adjust the response in the event the token is invalid.