Insecure session management - CSRF Fixation - Scala

Insecure session management - CSRF Fixation - Scala

Need

Secure session management and protection against CSRF fixation

Context

  • Usage of Scala for building scalable and high-performance applications
  • Usage of play.api.mvc for handling HTTP requests and responses in Play Framework
  • Usage of global execution context for Scala concurrent programming

Description

Non compliant code

        import play.api.mvc._
import scala.concurrent.ExecutionContext.Implicits.global

class TransactionController @Inject()(cc: ControllerComponents) extends AbstractController(cc) {

  def createTransaction() = Action.async { implicit request =>
    val publicKey = request.headers.get("Authorization").getOrElse("")
    val transaction = request.body.asJson.get.toString()
    
    // Creating a transaction with the provided public key
    Transaction.create(publicKey, transaction).map { transactionId =>
      Ok(Json.obj("transactionId" -> transactionId))
    }
  }
}
        
        

In the above code, the createTransaction method in TransactionController is handling the creation of transactions. Here, the public key is being fetched from the "Authorization" header and is used to create a transaction.

The vulnerability lies in the fact that the public key is always the same for payment links. This means that an attacker could potentially create a button with the content of a request and trick a user into running a transaction, receive the app push notification, and complete the request.

This is a CSRF (Cross-Site Request Forgery) vulnerability, where an attacker tricks a victim into performing actions that the victim did not intend to do. This can lead to potential unauthorized actions being performed on behalf of the victim.

In this case, the attacker could trick a user into creating a transaction without the user's knowledge or consent. The use of a static public key in the "Authorization" header makes it easier for an attacker to carry out this attack.

Steps

  • Implement CSRF protection mechanism in the application
  • Generate and include a CSRF token in each form or request that modifies state
  • Verify the CSRF token on the server-side for each request that modifies state
  • Ensure that the CSRF token is unique for each user session
  • Use secure and random CSRF tokens to prevent CSRF fixation attacks
  • Consider using the SameSite attribute for cookies to mitigate CSRF attacks
  • Regularly update and patch the Play framework and its dependencies to stay protected against known vulnerabilities

Compliant code

        import play.api.mvc._
import play.filters.csrf._
import play.filters.csrf.CSRF.Token
import scala.concurrent.ExecutionContext.Implicits.global

class TransactionController @Inject()(cc: ControllerComponents, csrfAddToken: CSRFAddToken, csrfCheck: CSRFCheck) extends AbstractController(cc) {

  def createTransaction() = csrfAddToken {
    Action.async { implicit request =>
      val publicKey = request.headers.get("Authorization").getOrElse("")
      val transaction = request.body.asJson.get.toString()
      
      // Get CSRF token from the request
      val csrfToken = CSRF.getToken(request).getOrElse("")
      
      // Verify the CSRF token
      if (csrfToken.value.nonEmpty && csrfToken.value == transaction.csrfToken) {
        // Creating a transaction with the provided public key
        Transaction.create(publicKey, transaction).map { transactionId =>
          Ok(Json.obj("transactionId" -> transactionId))
        }
      } else {
        Future.successful(BadRequest("Invalid CSRF token"))
      }
    }
  }
}
        
        

The updated code includes CSRF protection for the createTransaction method in the TransactionController class.

The CSRFAddToken and CSRFCheck classes are injected into the controller. These classes are used to add CSRF tokens to requests and to check the validity of CSRF tokens in requests, respectively.

In the createTransaction method, the csrfAddToken action composition is used to add a CSRF token to the request. The CSRF token is then retrieved from the request using the CSRF.getToken method.

The retrieved CSRF token is compared with the CSRF token included in the transaction data. If the tokens match, the transaction is created. If the tokens do not match, a BadRequest response is returned with the message "Invalid CSRF token".

This approach ensures that a unique CSRF token is included in each request that modifies state, and that this token is verified on the server-side. This helps to prevent CSRF fixation attacks.

In addition, consider using the SameSite attribute for cookies to further mitigate CSRF attacks. Regularly update and patch the Play framework and its dependencies to stay protected against known vulnerabilities.

References