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.