Documentation

You are viewing the documentation for the 2.5.18 release in the 2.5.x series of releases. The latest stable release series is 3.0.x.

§Protecting against Cross Site Request Forgery

Cross Site Request Forgery (CSRF) is a security exploit where an attacker tricks a victims browser into making a request using the victims session. Since the session token is sent with every request, if an attacker can coerce the victims browser to make a request on their behalf, the attacker can make requests on the users behalf.

It is recommended that you familiarise yourself with CSRF, what the attack vectors are, and what the attack vectors are not. We recommend starting with this information from OWASP.

There is no simple answer to what requests are safe and what are vulnerable to CSRF requests, the reason for this is that there is no clear specification as to what is allowable from plugins and future extensions to specifications. Historically, browser plugins and extensions have relaxed the rules that frameworks previously thought could be trusted, introducing CSRF vulnerabilities to many applications, and the onus has been on the frameworks to fix them. For this reason, Play takes a conservative approach in its defaults, but allows you to configure exactly when a check is done. By default, Play will require a CSRF check when all of the following are true:

Note: If you use browser-based authentication other than using cookies or HTTP authentication, such as NTLM or client certificate based authentication, then you must set play.filters.csrf.header.protectHeaders = null, or include the headers used in authentication in protectHeaders.

§Play’s CSRF protection

Play supports multiple methods for verifying that a request is not a CSRF request. The primary mechanism is a CSRF token. This token gets placed either in the query string or body of every form submitted, and also gets placed in the users session. Play then verifies that both tokens are present and match.

To allow simple protection for non browser requests, Play only checks requests with cookies in the header. If you are making requests with AJAX, you can place the CSRF token in the HTML page, and then add it to the request using the Csrf-Token header.

Alternatively, you can set play.filters.csrf.header.bypassHeaders to match common headers: A common configuration would be:

This configuration would look like:

play.filters.csrf.header.bypassHeaders {
  X-Requested-With = "*"
  Csrf-Token = "nocheck"
}

Caution should be taken when using this configuration option, as historically browser plugins have undermined this type of CSRF defence.

§Trusting CORS requests

By default, if you have a CORS filter before your CSRF filter, the CSRF filter will let through CORS requests from trusted origins. To disable this check, set the config option play.filters.csrf.bypassCorsTrustedOrigins = false.

§Applying a global CSRF filter

Play provides a global CSRF filter that can be applied to all requests. This is the simplest way to add CSRF protection to an application. To enable the global filter, add the Play filters helpers dependency to your project in build.sbt:

libraryDependencies += filters

Now add them to your Filters class as described in HTTP filters:

import play.api.http.DefaultHttpFilters
import play.filters.csrf.CSRFFilter
import javax.inject.Inject

class Filters @Inject() (csrfFilter: CSRFFilter)
  extends DefaultHttpFilters(csrfFilter)

The Filters class can either be in the root package, or if it has another name or is in another package, needs to be configured using play.http.filters in application.conf:

play.http.filters = "filters.MyFilters"

§Getting the current token

The current CSRF token can be accessed using the CSRF.getToken method. It takes an implicit RequestHeader, so ensure that one is in scope.

val token: Option[CSRF.Token] = CSRF.getToken

If you are not using the CSRF filter, you also should inject the CSRFAddToken and CSRFCheck action wrappers to force adding a token or a CSRF check on a specific action. Otherwise the token will not be available.

import play.api.mvc._
import play.api.mvc.Results._
import play.filters.csrf._
import play.filters.csrf.CSRF.Token

class CSRFController(addToken: CSRFAddToken, checkToken: CSRFCheck) extends Controller {
  def getToken = addToken(Action { implicit request =>
    val Token(name, value) = CSRF.getToken.get
    Ok(s"$name=$value")
  })
}

To help in adding CSRF tokens to forms, Play provides some template helpers. The first one adds it to the query string of the action URL:

@import helper._

@form(CSRF(routes.ItemsController.save())) {
    ...
}

This might render a form that looks like this:

<form method="POST" action="/items?csrfToken=1234567890abcdef">
   ...
</form>

If it is undesirable to have the token in the query string, Play also provides a helper for adding the CSRF token as hidden field in the form:

@form(routes.ItemsController.save()) {
    @CSRF.formField
    ...
}

This might render a form that looks like this:

<form method="POST" action="/items">
   <input type="hidden" name="csrfToken" value="1234567890abcdef"/>
   ...
</form>

The form helper methods all require an implicit RequestHeader to be available in scope. This will typically be provided by adding an implicit RequestHeader parameter to your template, if it doesn’t have one already.

§Adding a CSRF token to the session

To ensure that a CSRF token is available to be rendered in forms, and sent back to the client, the global filter will generate a new token for all GET requests that accept HTML, if a token isn’t already available in the incoming request.

§Applying CSRF filtering on a per action basis

Sometimes global CSRF filtering may not be appropriate, for example in situations where an application might want to allow some cross origin form posts. Some non session based standards, such as OpenID 2.0, require the use of cross site form posting, or use form submission in server to server RPC communications.

In these cases, Play provides two actions that can be composed with your applications actions.

The first action is the CSRFCheck action, and it performs the check. It should be added to all actions that accept session authenticated POST form submissions:

import play.api.mvc._
import play.filters.csrf._

def save = checkToken {
  Action { req: RequestHeader =>
    // handle body
    Ok
  }
}

The second action is the CSRFAddToken action, it generates a CSRF token if not already present on the incoming request. It should be added to all actions that render forms:


import play.api.mvc._ import play.filters.csrf._ def form = addToken { Action { implicit req: RequestHeader => Ok(views.html.itemsForm) } }

A more convenient way to apply these actions is to use them in combination with Play’s action composition:

import play.api.mvc._
import play.filters.csrf._

object PostAction extends ActionBuilder[Request] {
  def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
    // authentication code here
    block(request)
  }
  override def composeAction[A](action: Action[A]) = checkToken(action)
}

object GetAction extends ActionBuilder[Request] {
  def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
    // authentication code here
    block(request)
  }
  override def composeAction[A](action: Action[A]) = addToken(action)
}

Then you can minimise the boiler plate code necessary to write actions:

def save = PostAction {
  // handle body
  Ok
}

def form = GetAction { implicit req =>
  Ok(views.html.itemsForm)
}

§CSRF configuration options

The full range of CSRF configuration options can be found in the filters reference.conf. Some examples include:

Next: Custom Validations