Implementation of proper input validation and sanitization to prevent path traversal attacks
import play.api.mvc._
import play.api.libs.Files
import java.nio.file.Paths
class ApplicationController extends Controller {
def downloadFile(fileName: String) = Action {
val file = Paths.get("/app/files/", fileName).toFile
if (file.exists) {
Ok.sendFile(file)
} else {
NotFound
}
}
}
The
downloadFile function in the
ApplicationController class takes a
fileName parameter from the user and uses it to construct a file path. This file path is then used to fetch a file from the server's file system and send it back to the user.
This code is vulnerable to Path Traversal attacks because it does not validate or sanitize the
fileName parameter before using it to construct the file path. An attacker can provide a
fileName parameter that includes special characters or sequences (like
../) to traverse the file system and access files outside of the intended directory.
For example, an attacker can request
/downloadFile?fileName=../../etc/passwd to download the
/etc/passwd file, which contains sensitive information about every user on the system.
This vulnerability can lead to unauthorized access to sensitive data, corruption or deletion of data, or other unintended behavior. It is critical to always validate and sanitize user input before using it to interact with the file system.
import play.api.mvc._
import play.api.libs.Files
import java.nio.file.{Paths, Path, Files => JFiles}
import java.io.File
class ApplicationController extends Controller {
def downloadFile(fileName: String) = Action {
val sanitizedFileName = fileName.replaceAll("[^a-zA-Z0-9.-]", "")
val file = Paths.get("/app/files/", sanitizedFileName).normalize().toFile
val appDir = new File("/app/files/").getCanonicalPath
if (file.getCanonicalPath.startsWith(appDir) && file.exists) {
Ok.sendFile(file)
} else {
NotFound
}
}
}
The updated code includes a fix for the path traversal vulnerability. The fix involves sanitizing the
fileName parameter and validating the constructed file path.
1. Sanitizing the
fileName parameter: The
fileName parameter is sanitized by removing any characters that are not alphanumeric, a period (.), or a hyphen (-). This is done using the
replaceAll method with a regular expression that matches any character that is not in the specified set. This helps to prevent any special characters or sequences that could be used for path traversal from being included in the
fileName.
val sanitizedFileName = fileName.replaceAll("[^a-zA-Z0-9.-]", "")
..) to their absolute path equivalents. Checking that the normalized path starts with the expected directory path ensures that the file path does not point to a location outside of the intended directory.
val file = Paths.get("/app/files/", sanitizedFileName).normalize().toFile
val appDir = new File("/app/files/").getCanonicalPath
if (file.getCanonicalPath.startsWith(appDir) && file.exists) {
Ok.sendFile(file)
} else {
NotFound
}
downloadFile method is not vulnerable to path traversal attacks, as it properly validates and sanitizes the
fileName parameter and the constructed file path.