Skip to main content

Cross-site request forgery

Need

Protection against Cross-Site Request Forgery (CSRF) attacks

Context

  • Usage of Scala for building scalable and high-performance applications
  • Usage of play.api.mvc for handling HTTP requests and building web applications in Play Framework

Description

Non compliant code

import play.api.mvc._

object Application extends Controller {

def doSomething = Action { implicit request =>
val userId = request.session.get("userId")
userId.map { user =>
// Perform some action on behalf of the user
}
Ok("Done")
}
}

In the above code, we have a doSomething action in a controller that performs some action on behalf of the user. The user's identity is determined from the session, which is typically stored in a cookie.

The vulnerability here is that there is no protection against cross-site request forgery (CSRF). An attacker can trick a victim into making a request to this action without their knowledge or consent. Since the user's identity is determined solely by the session cookie, which is automatically included with every request, the application has no way of distinguishing between legitimate requests and forged requests.

For example, an attacker could include a link or a form in a webpage that makes a POST request to this action. If the victim visits the attacker's webpage while logged into the application, their browser will automatically include their session cookie with the request, and the application will perform the action on behalf of the attacker.

In this way, the attacker can perform actions on behalf of the victim without their consent. This is a serious security vulnerability that can lead to unauthorized access or changes to data, among other things.

Steps

  • Implement Cross-Site Request Forgery (CSRF) protection
  • Generate and include a CSRF token in each form
  • Verify the CSRF token on each request that modifies data
  • Use the CSRF protection configuration provided by the Play framework

Compliant code

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

object Application extends Controller {

def doSomething = CSRFCheck {
Action { implicit request =>
val userId = request.session.get("userId")
userId.map { user =>
// Perform some action on behalf of the user
}
Ok("Done")
}
}
}

The updated code includes the use of CSRFCheck which is a filter provided by the Play framework to protect against Cross-Site Request Forgery (CSRF) attacks. This filter checks for the presence of a valid CSRF token in each request that modifies data. If the token is not present or is invalid, the request is rejected.

The CSRFCheck filter is applied to the doSomething action, which is the action that was vulnerable to CSRF attacks. Now, before the action is executed, the CSRF token in the request is checked. If the token is valid, the action is executed. If the token is not valid, the request is rejected.

This way, an attacker cannot trick authenticated users into executing actions without their consent, because they would need a valid CSRF token, which they cannot obtain.

Remember to include the CSRF token in each form in your views. You can do this by adding @CSRF.formField in your form. This will generate a hidden input field containing the CSRF token.

<form method="post" action="@routes.Application.doSomething()">
@CSRF.formField
<!-- rest of your form fields -->
</form>

This ensures that each form submission includes a valid CSRF token. The CSRFCheck filter then verifies this token when the form is submitted.

References