Play2 - implementing a CSRF token mechanism
Recently I stumbled over a security issue with some of my web applications. So I decided to dig a bit deeper in how play2 in version 2.3.x handles cross site request forgery attacks and a came up with this little article.
Since version 2.1 of the play framework it has the ability to use filters and with these filters they also support CSRF tokens.
I will give a brief introduction to what is necessary for a play2 application to protect against this vulnerability.
First of all we need the filter dependency for scalas build tool - sbt. So include it in plays build.sbt config file.
libraryDependencies += filters
alternative you can put the configuration in an already existing dependency sequence:
librarayDependencies ++= Seq(..., filters, ...)
Second we need to modify the Global-Object for our application. So extend it from a class called WithFilters. I also added the SecurityHeadersFilter but this isn’t necessary for this example.
object Global extends WithFilters(CSRFFilter(), SecurityHeadersFilter()) with GlobalSettings { ... }
So the Global-Object extends WithFilters and adds the trait GlobalSettings. In the WithFilters class we add a CSRFFilter and a SecurityHeadersFilter.
Now we can prepare our actions in our controller class:
In scala we need an implicit parameter and a CSRF-Token which we can obtain on the CSRF Object with the getToken(request)
method.
This method needs an implicit request we can hand over to the CSRF class. This generated token will then be given to our template
where we use the token to protect our forms.
The code for a simple controller could look like this:
package controllers
import play.api.mvc._
object Application extends Controller {
def index = Action { implicit request =>
import play.filters.csrf._ val token = CSRF.getToken(request)
Ok(views.html.index())
}
def process = Action { implicit request =>
Ok("Success")
}
}
The last step would be to provide the template with our newly created token. For this to work we need the implicit request headers. So add an implicit request header to the template. I also imported the helper package for convenience:
@()(implicit requestHeader: RequestHeader)
@import helper._
The form tag can be created with one of the imported helpers:
@form(CSRF(routes.Application.process()), 'enctype -\> "multipart/form-data") {
}
@form(routes.Application.process(), 'enctype -\> "multipart/form-data") {
@CSRF.formField
}
That’s all we need to implement the mechanism. I will now show you some configuration options to customize the tokens.
Put these configurations in the application.conf
file. The options listed below are the defaults, so feel free to
override them for your needs.
# CSRF configuration
csrf.token.name=csrfToken
csrf.cookie.name=csrfCookie
csrf.cookie.secure=true
csrf.sign.tokens=true
Earlier I mentioned the SecurityHeaderFilter
which sets a few useful HTTP headers. Below are the default settings if
you don’t override or set them. These settings are the most restrictive ones and can be set in the application.conf
file.
These are the headers you can set:
- Content-Security-Policy:default-src ‘self’
- X-Content-Type-Options:nosniff
- X-Frame-Options:DENY
- X-Permitted-Cross-Domain-Policies:master-only
- X-XSS-Protection:1; mode=block
And here are the configurations for them:
# Security headers this are the defaults
play.filters.headers.frameOptions=DENY
play.filters.headers.xssProtection="1; mode=block"
play.filters.headers.contentTypeOptions="nosniff"
play.filters.headers.permittedCrossDomainPolicies="master-only"
play.filters.headers.contentSecurityPolicy="default-src"
I hope this short article will help someone with similar problems.