Server side cross-site scripting - Scala

Server side cross-site scripting - Scala

Need

Prevention of server-side cross-site scripting attacks

Context

  • Usage of Scala 2.13 for developing scalable and high-performance applications
  • Usage of javax.inject for Dependency Injection in Java applications
  • Usage of play.api.mvc for handling HTTP requests and responses in Play Framework
  • Usage of play.api.mvc.ControllerComponents for handling HTTP requests and building controllers in the Play Framework
  • Usage of play.api.mvc.BaseController for creating base controllers in Play Framework
  • Usage of play.api.mvc.Request for handling HTTP requests in Play Framework
  • Usage of play.api.mvc.AnyContent for handling any type of content in Play Framework
  • Usage of play.api.mvc.Action for handling HTTP requests in Play Framework
  • Usage of play.api.mvc.Request for handling HTTP requests in Play Framework
  • Usage of play.api.mvc.AnyContent for handling any type of content in Play Framework
  • Usage of play.twirl.api.Html for generating HTML templates in Play Framework
  • Usage of play.api.mvc.Result for handling HTTP responses in Play Framework

Description

Non compliant code

        package controllers

import javax.inject._
import play.api.mvc._

@Singleton
class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
  
  def index() = Action { implicit request: Request[AnyContent] =>
    val userInput = request.getQueryString("userInput").getOrElse("")
    Ok(views.html.index(userInput))
  }
}
        
        

The above code is a simple Scala Play Framework application that takes user input from the query string and passes it to a template to be rendered. The vulnerability lies in the fact that the user input is directly passed into a JavaScript eval() function in the HTML template without any sanitization or encoding.

When the server receives a request, the index() function in the HomeController is called. This function retrieves the userInput from the request's query string. If no userInput is provided, it defaults to an empty string.

The user input is then passed to the index view template, where it is directly inserted into a JavaScript eval() function. The eval() function in JavaScript evaluates or executes an argument if it's a JavaScript expression or statement. This means that any JavaScript code passed in the userInput query parameter will be executed when the page is loaded in the browser.

This opens up the possibility for a Cross-Site Scripting (XSS) attack, where an attacker could inject malicious JavaScript code as the userInput that would then be executed on the server side. This could lead to various harmful outcomes, such as compromising internal server files, making requests on behalf of the server, or performing a port scan.

Steps

  • Sanitize user input: Implement input validation and filtering to remove any potentially malicious code.
  • Use proper output encoding: Ensure that user input is properly encoded before rendering it in the HTML response.
  • Implement Content Security Policy (CSP): Set up a Content Security Policy to restrict the types of content that can be loaded by the application.
  • Enable XSS protection mechanisms: Enable built-in XSS protection mechanisms provided by the framework or web server.
  • Educate developers: Provide training and guidelines to developers on secure coding practices, including the risks and prevention of XSS vulnerabilities.

Compliant code

        package controllers

import javax.inject._
import play.api.mvc._
import play.twirl.api.HtmlFormat

@Singleton
class HomeController @Inject()(val controllerComponents: ControllerComponents) extends BaseController {
  
  def index() = Action { implicit request: Request[AnyContent] =>
    val userInput = request.getQueryString("userInput").getOrElse("")
    val sanitizedInput = HtmlFormat.escape(userInput).toString
    Ok(views.html.index(sanitizedInput))
  }
}
        
        

The original code was vulnerable to a Server Side Cross-Site Scripting (XSS) attack. This is because it directly used the user input in the HTML response without sanitizing or encoding it. An attacker could inject malicious JavaScript code as user input, which would then be executed by the server.

The fixed code mitigates this vulnerability by sanitizing the user input before using it in the HTML response. This is done using the HtmlFormat.escape method provided by the Play framework. This method escapes special characters in the input string that could be used to inject malicious code.

In addition to this code fix, it is recommended to implement a Content Security Policy (CSP) to restrict the types of content that can be loaded by the application. This can help prevent XSS attacks by disallowing the loading of scripts from unauthorized sources.

Furthermore, developers should be educated on secure coding practices, including the risks and prevention of XSS vulnerabilities. This can help prevent such vulnerabilities from being introduced in the first place.

References