Insecure or unset HTTP headers - Accept - Scala

Insecure or unset HTTP headers - Accept - Scala

Need

Enforcement of secure and specific Accept headers

Context

  • Usage of Scala for building scalable and functional applications
  • Usage of play.api.mvc for handling HTTP requests and responses in Play Framework

Description

Non compliant code

        import play.api.mvc._

object ApplicationController extends Controller {
  def index = Action { implicit request =>
    Ok("Hello World!")
  }
}
        
        

In the above Scala code, a basic server is set up using the Play Framework. The server has an endpoint at the root ("/") which responds to HTTP requests. The issue here is that the server does not set the Accept header for the requests it processes.

The Accept header is important as it tells the server what media types are acceptable for the client. This is important as it allows the server to send a response in a format that the client can understand and process.

In the current state of the code, the server does not specify an Accept header, which means that it will accept any media type. This can lead to a vulnerability where an attacker may send a request with a media type that causes unexpected behavior when the server tries to process it. This could potentially lead to a range of issues, from server errors to security vulnerabilities.

Steps

  • Implement input validation to ensure that the Accept header is set and contains a valid MIME type.
  • Restrict the allowed MIME types in the Accept header to only those that are expected by the application.
  • Handle invalid or unexpected MIME types in a secure manner, such as returning an appropriate error response or rejecting the request.
  • Consider implementing content negotiation to allow the application to respond with the most appropriate representation based on the client's Accept header.

Compliant code

        import play.api.mvc._
import play.api.http._

object ApplicationController extends Controller {
  def index = Action(parse.tolerantText) { implicit request =>
    request.headers.get("Accept") match {
      case Some(mime) if mime == "application/json" || mime == "text/html" =>
        Ok("Hello World!")
      case _ =>
        BadRequest("Invalid or missing Accept header")
    }
  }
}
        
        

The updated code now includes a check for the Accept header in the HTTP request. This is done using the request.headers.get("Accept") method, which retrieves the value of the Accept header if it exists.

The match statement is used to handle different cases based on the value of the Accept header. If the Accept header is set and its value is either application/json or text/html, the request is processed and the response "Hello World!" is returned. These are the MIME types that the application expects and can handle.

If the Accept header is missing or its value is not one of the expected MIME types, the request is rejected and a BadRequest response is returned with the message "Invalid or missing Accept header". This ensures that the application behaves securely when it encounters an invalid or unexpected MIME type.

The parse.tolerantText method is used to parse the request body as text, allowing for a more flexible handling of different content types. This is an example of content negotiation, where the application can respond with the most appropriate representation based on the client's Accept header.

This approach helps to mitigate the risk of an attacker causing unexpected behaviors by sending requests with incorrect content types. It ensures that the application only processes requests with expected and valid MIME types, thereby enhancing its security.

References