CSRF protection with AngularJS and Mojolicious

Gainstrack is a single page application based on AngularJS that makes Javascript calls to a Mojolicious backend. In the modern age, one just protect against CSRF attacks that change user state triggered by malicious code in a pages on a 3rd party website.

A good first line of defence is to ensure that all requests that may change state are restricted to POST requests. This is as simple as ensuring your route accepts nothing but post

$r->post('/command/doStuff') # Not $r->any

This protects against many attacks caused from sites with XSS vulnerabilities allowing hackers to place carefully crafted IMG tags on such vulnerable sites. However, it doesn’t stop carefully crafted iframes+javascript from executing a POST attack as this Stackoverflow question demonstrates.

For further protection, we should generate a server side token that the client side passes back only via Javascript. This provides protection against CSRF because the Javascript that can process the token is protected by same origin policy. There are various intricacies in implementing CSRF token properly. Fortunately, both AngularJS and Mojolicious have in-built help for CSRF protection – it is just a matter of gluing things together!

Angular $http documentation describes how it has in-built support to use a XSRF-TOKEN cookie to subsequently add the token back via a X-XSRF-TOKEN header. So we just need to support this protocol on the Mojolicious side. Fortunately, Mojolicious already has token generation support. Thus all you need on one your app entry points is to have

$c->cookie('XSRF-TOKEN' => $c->csrf_token, {path => '/'});

Angular will handle this automatically. To perform CSRF validation, simply compare the header

my $csrf = $c->req->headers->header('X-XSRF-TOKEN') // '';
($csrf eq $c->csrf_token) or die("CSRF attack was foiled");

And with no further change on the client side, you have CSRF protection! Not bad for 3 lines of code.

Note that the above doesn’t seem to work of the site is deployed via Cordova/PhoneGap application. I had to do a workaround for that… details available upon request.

Leave a Reply

Your email address will not be published. Required fields are marked *