Secure file upload and validation
import play.api.mvc._
import play.api.libs.Files._
import java.io.File
object FileController extends Controller {
def upload = Action(parse.temporaryFile) { request =>
request.body.moveTo(new File("/path/to/uploaded/files/" + request.body.filename))
Ok("File uploaded")
}
}
In the above code, we have a simple Play Framework controller that accepts file uploads. The
upload
action is handling a multipart/form-data request, where the file is temporarily stored and then moved to a specific directory.
The vulnerability here is that there's no validation or sanitization of the uploaded files. This means:
1. Files with any extension can be uploaded, including potentially harmful ones like
.html
,
.php
,
.exe
, etc. These can be used for code injection, remote code execution, or other malicious activities.
2. Files with double extensions can be uploaded. For example, an attacker could upload a file named
safe.txt.exe
. The system might only check the last extension and consider it safe, while the operating system executes it as an executable file.
3. There's no validation of the file content. An attacker could upload a file containing malicious code, which could be executed if the file is interpreted by a server-side script interpreter (e.g., if the file is
.php
and the server is configured to run PHP scripts).
This is a serious security vulnerability, as it could allow an attacker to execute arbitrary code on the server, potentially leading to full system compromise.
import play.api.mvc._
import play.api.libs.Files._
import java.io.File
import java.nio.file.{Files, Paths}
object FileController extends Controller {
val allowedExtensions = List(".jpg", ".png", ".gif")
def upload = Action(parse.temporaryFile) { request =>
val filename = request.body.filename
val extension = filename.substring(filename.lastIndexOf("."))
if (!allowedExtensions.contains(extension)) {
BadRequest("Invalid file extension")
} else if (Files.probeContentType(Paths.get(filename)) == "application/x-msdownload") {
BadRequest("Executable files are not allowed")
} else {
request.body.moveTo(new File("/secure/path/to/uploaded/files/" + filename), replace = true)
Ok("File uploaded")
}
}
}
The updated code includes a list of allowed file extensions. When a file is uploaded, the code checks the file's extension against this list. If the file's extension is not in the list, the server responds with a "Bad Request" error.
The code also checks the file's MIME type using the
Files.probeContentType
method. If the MIME type is
application/x-msdownload
, which is commonly associated with executable files, the server responds with a "Bad Request" error.
Finally, the code moves the uploaded file to a secure location. The
replace = true
option ensures that if a file with the same name already exists in the destination directory, the existing file will be replaced with the uploaded file. This prevents an attacker from uploading a file with the same name as an existing file in an attempt to overwrite the existing file.
This solution addresses the insecure file upload vulnerability by ensuring that only files with allowed extensions can be uploaded, and by preventing the upload of executable files. It also secures the storage of uploaded files by using a secure file storage location and by replacing existing files with the same name.