Skip to main content

Inadequate file size control

Need

Implementation of file size validation and restriction measures

Context

  • Usage of Scala 2.13 for building scalable and high-performance applications
  • Usage of play.api.mvc for handling HTTP requests and building web applications in Play Framework
  • Usage of play.api.libs.Files for file handling in a Play Framework application

Description

Non compliant code

import play.api.mvc._
import play.api.libs.Files

def upload = Action(parse.multipartFormData) { request =>
request.body.file("picture").map { picture =>
val filename = picture.filename
val contentType = picture.contentType
picture.ref.moveTo(Paths.get(s"/tmp/picture/$filename"), replace = true)
Ok("File uploaded")
}.getOrElse {
Redirect(routes.HomeController.index).flashing(
"error" -> "Missing file"
)
}
}

The above Scala code snippet is a simple file upload function in a Play framework application. It uses multipartFormData parser to handle file uploads in a HTTP request. The request.body.file("picture") is used to fetch the file from the request, and then the file is moved to a designated location on the server using picture.ref.moveTo.

The vulnerability here is that there is no check on the size of the uploaded file. This allows a user to upload a file of any size to the server. This can lead to various issues such as:

  1. Storage space exhaustion: If users keep uploading large files, the server's storage space could be exhausted, leading to denial of service.
  2. Memory overflow: Large files occupy more memory. If the server has limited memory, uploading a large file could cause the server to run out of memory, leading to crashes or slow performance.
  3. Network congestion: Uploading large files consumes more bandwidth. If the server's network capacity is limited, this could lead to network congestion, slowing down the server and affecting other users' experience.

In the worst case scenario, an attacker could deliberately upload extremely large files to exhaust the server's resources and bring down the system, which is a form of Denial of Service (DoS) attack.

Steps

  • Implement a maximum file size limit for file uploads.
  • Validate the file size before saving it to the server.
  • Handle the case when the file size exceeds the limit and provide appropriate error messages to the user.
  • Consider using a configuration parameter to define the maximum file size limit.
  • Ensure that the file size limit is enforced both on the client-side and server-side.
  • Consider using a library or framework feature that provides built-in file size control and validation.

Compliant code

import play.api.mvc._
import play.api.libs.Files
import play.api.http.HttpErrorHandler
import play.api.mvc.MultipartFormData.FilePart
import play.api.libs.streams.Accumulator
import play.core.parsers.Multipart.FileInfo
import java.nio.file.Paths

// Define maximum file size
val MAX_FILE_SIZE: Long = 1024 * 1024 * 10 // 10MB

def upload = Action(parse.maxLength(MAX_FILE_SIZE, parse.multipartFormData)) { request =>
request.body match {
case Left(MaxSizeExceeded(length)) =>
BadRequest("File too large")

case Right(multipartForm) =>
multipartForm.file("picture").map { picture =>
val filename = picture.filename
val contentType = picture.contentType
picture.ref.moveTo(Paths.get(s"/tmp/picture/$filename"), replace = true)
Ok("File uploaded")
}.getOrElse {
Redirect(routes.HomeController.index).flashing(
"error" -> "Missing file"
)
}
}
}

The updated code now includes a maximum file size limit for file uploads. The parse.maxLength method is used to enforce this limit. If the size of the uploaded file exceeds this limit, the Left(MaxSizeExceeded(length)) case will match and a BadRequest response with the message "File too large" will be returned to the client.

The Right(multipartForm) case matches when the file size is within the limit. The file is then processed as before: its filename and content type are retrieved, and it is moved to the /tmp/picture/ directory on the server.

The MAX_FILE_SIZE constant is used to define the maximum file size limit. This is currently set to 10MB, but it can be adjusted as needed.

This solution ensures that the file size limit is enforced on the server-side. For a complete solution, the file size limit should also be enforced on the client-side. This can be done using JavaScript in the browser, or by configuring the client if it is a separate application.

References