Disabling the CSRF filter for a single action in Play Framework
Playframework includes a filter which enables protection against CSRF (Cross Site Request Forgery) attacks.
This filter can easily be enabled for all request by adding the CSRFFilter to the filter-chain in Global.scala.
Example:
object Global extends WithFilters(CSRFFilter()) with GlobalSettings {}
Or by applying the filtering on a per action basis.
Example:
def myAction = CSRFCheck {
Action { req =>
Ok
}
}
Having these options are probably enough for most cases, but what if you want to just exclude one single action from the CSRF filter?
Surely there must be a way for doing this although there is no built in support in Play for this case.
I had this problem recently and came up with the following solution which is based on handling the filter-chain in a similar way that Play does it but giving the ability to select the filters to apply based on the request-header. By doing that the path can be used for determining which filters to use. This was good enough for my use case of disabling the CSRFFilter for one Action.
Here’s a code example for the Global object:
object Global extends GlobalSettings {
val csrfFilter = CSRFFilter()
val defaultFilters = List(csrfFilter)
def filters(rh: RequestHeader) = {
if (rh.path.startsWith("/path-where-to-bypass-csrf-filter")) defaultFilters.filterNot(_.eq(csrfFilter))
else defaultFilters
}
override def doFilter(a: EssentialAction): EssentialAction = {
FilterChainByRequestHeader(super.doFilter(a), filters)
}
}
Here’s the code for the modified FilterChain which takes a function instead of a list of filters:
/**
* This is based on the play.api.mvc.FilterChain to be able to determine the list of filters to apply based on
* the request header
*/
object FilterChainByRequestHeader {
def apply[A](action: EssentialAction,
filtersFun: (RequestHeader) => List[EssentialFilter]): EssentialAction = new EssentialAction {
def apply(rh: RequestHeader): Iteratee[Array[Byte], SimpleResult] = {
val chain = filtersFun(rh).reverse.foldLeft(action) { (a, i) => i(a) }
chain(rh)
}
}
}
With the above changes, the path /path-where-to-bypass-csrf-filter will not be checked for CSRF and the goal is accomplished.