Selectively Disable-able Java CSRF Filters in Play! 2.5

I’ve written a little bit about CSRF (Cross-Site Request Forgery) protection before. Play provides strong CSRF protection out of the box, which is one of the things I like about the framework. To enable across-the-board CSRF protection, you create the following file, at app/filters/Filters.java:

Then enable the CSRF filter in application.conf:

And add a dependency for filters to build.sbt:

This will apply Play’s default, strong CSRF protection to every request which contains any cookies in the header.

In some cases, though, it’s important to be able to let cross-site requests happen for some (but not all) requests. In this case, ‘forgery’ is a misnomer, because we want the user to be able to make a request from our world-facing app, hit a microservice (for, say, authentication), and see results rather than being unceremoniously kicked out by an error message.

The most straightforward way to do that is with a blacklist: instead of you add an @RequireCSRFCheck annotation to all the form post methods which require CSRF filtering, and an @AddCSRFToken annotation to the action methods which generate those forms. This is tedious if you have many methods which require CSRF filtering and few that don’t. It’s also error-prone, and any oversight results in your security failing open rather than closed, which is not what we want.

Fortunately, although it’s not quite as straightforward, we do still have the ability to selectively disable a global CSRF filter, with just a little work. Moreover, we can do it in Java!

Steve Chaloner provides an excellent answer on StackOverflow detailing how to decorate Play’s routes file with comments to disable CSRF on a per-action basis. At SoFi, we like annotations, so we did it a little bit differently.

First, define an annotation:

We’ll use this annotation on any action method we don’t want to protect against CSRF.

Having defined the annotation, we then define a custom CSRF filter, which will replace the CSRFFilter in Filters.java. Save it as app/filters/AnnotationDisablableCSRFFilter.java:

The important distinction between my solution here and Steve’s solution from the stackoverflow answer is that we’re grabbing the class and method to which the request was made, then inspecting it for the presence of our @DisableCSRFCheck annotation.

Having implemented our custom filter, we then update the constructor of Filters.java, replacing the default CSRFFilter with our AnnotationDisablableCSRFFilter:

Whereupon we can go through and apply the @DisableCSRFCheck annotation to the action methods we wish to open to cross-site requests.