External control of file name or path - Scala

External control of file name or path - Scala

Need

Secure file handling and storage

Context

  • Usage of Scala 2.12 for building scalable and high-performance applications
  • Usage of play.api.mvc for building web applications with the Play Framework
  • Usage of java.io.File for file input/output operations

Description

Non compliant code

        import play.api.mvc._

def upload = Action(parse.multipartFormData) { request =>
  request.body.file("picture").map { picture =>
    import java.io.File
    val filename = picture.filename
    val contentType = picture.contentType
    picture.ref.moveTo(new File(s"/user/uploads/$filename"))
    Ok("File uploaded")
  }.getOrElse {
    Redirect(routes.Application.index).flashing(
      "error" -> "Missing file"
    )
  }
}
        
        

The above Scala function upload is handling file uploads in a Play framework application. It is using multipartFormData to parse the incoming request.

The vulnerability lies in this line of code:

                picture.ref.moveTo(new File(s"/user/uploads/$filename"))

            


Here, the application is saving the uploaded file to a directory based on the filename provided by the user. This is a direct example of the External Control of File Name or Path vulnerability. The filename variable is directly controlled by the user and is not sanitized or validated before being used to move the file, which means an attacker could potentially manipulate the filename to alter the path of the saved file.

For example, an attacker could provide a filename like ../../etc/passwd, which could overwrite important system files if the application has the necessary permissions, leading to serious security implications.

In addition, if the application is storing sensitive information in the /user/uploads/ directory, an attacker could potentially access this information by guessing the filenames.

This vulnerability can lead to unauthorized file upload, directory traversal and potential Remote Code Execution (RCE) if the uploaded files are executable.

Steps

  • Validate the file name and path before saving it.
  • Use a whitelist approach to restrict the allowed characters and file extensions.
  • Sanitize the file name to remove any potentially malicious characters or sequences.
  • Generate a unique file name to prevent overwriting existing files.
  • Store the uploaded files in a secure directory outside of the web root.
  • Implement proper access controls to restrict unauthorized access to the uploaded files.
  • Consider using a file storage service or database instead of saving files directly to the file system.

Compliant code

        import play.api.mvc._
import java.nio.file.{Paths, Files}
import java.util.UUID

def upload = Action(parse.multipartFormData) { request =>
  request.body.file("picture").map { picture =>
    val filename = picture.filename
    val contentType = picture.contentType
    val sanitizedFileName = filename.replaceAll("[^a-zA-Z0-9\\\\.\\\\-]", "_")
    val uniqueFileName = UUID.randomUUID().toString + "_" + sanitizedFileName
    val secureDirectory = "/secure/uploads/"
    val filePath = Paths.get(secureDirectory, uniqueFileName)
    if (!Files.exists(filePath)) {
      picture.ref.moveTo(filePath.toFile)
      Ok("File uploaded")
    } else {
      InternalServerError("Error in file upload")
    }
  }.getOrElse {
    Redirect(routes.Application.index).flashing(
      "error" -> "Missing file"
    )
  }
}
        
        

The updated code includes several measures to mitigate the risk of external control of file name or path vulnerability.

1. File Name Sanitization: The file name is sanitized by replacing any character that is not alphanumeric, a period, or a hyphen with an underscore. This prevents any potentially malicious characters or sequences from being included in the file name.

2. Unique File Name Generation: A unique file name is generated by appending a UUID to the sanitized file name. This prevents overwriting of existing files.

3. Secure Directory: The uploaded files are saved in a secure directory ( /secure/uploads/). This directory should be outside of the web root to prevent direct access to the uploaded files.

4. File Existence Check: Before moving the file, the code checks if a file with the same name already exists in the directory. If it does, an error is returned.

5. Error Handling: If the file is missing, the user is redirected to the index page with an error message.

Remember to set proper access controls on the /secure/uploads/ directory to restrict unauthorized access. Consider using a file storage service or database for storing files if your application handles a large number of file uploads or requires more advanced features.

References