Insecure File Management
Fixing Insecure File Management
About Path Traversal
What is Path Traversal?
Path traversal, also known as directory traversal, is a type of security vulnerability that allows an attacker to access files or directories outside the web root directory of a web application. This vulnerability occurs when a web application does not properly validate user input or user-controlled data, such as file names or paths, and allows directory traversal sequences to be passed through the input.
Check out this video for a high-level explanation:
What is the impact of Path Traversal?
Path Traversal can lead to unauthorized access to sensitive files and data on a server or application. This can include files containing passwords, user data, or other confidential information.
An attacker who successfully exploits a Path Traversal vulnerability can gain access to sensitive data, modify or delete files, and potentially execute arbitrary code or perform other malicious actions.
This can lead to a variety of security incidents, such as data breaches, theft of intellectual property, or disruption of services.
The impact of a Path Traversal vulnerability can vary depending on the nature of the files and data that are accessed, as well as the specific context of the system or application being targeted.
In some cases, a Path Traversal attack may have little impact beyond revealing the existence of certain files, while in other cases, it can have severe consequences for the confidentiality, integrity, and availability of the system or application.
How to prevent Path Traversal?
To prevent Path Traversal vulnerabilities, it is important to properly validate and sanitize user input, and to use secure coding practices.
Here are some steps that can help prevent Path Traversal attacks:
- Validate user input: Always validate and sanitize user input, especially file names and paths. Ensure that user input only contains expected characters and that any special characters are properly escaped or removed.
- Use secure coding practices: Use secure coding practices, such as enforcing strict file permissions and preventing the execution of user-controlled input as code. Use libraries and frameworks that have built-in protection against Path Traversal attacks.
- Use an allow list: Use an allow list of permitted file names or directories and reject any input that does not match the list. This can help prevent unauthorized access to sensitive files and directories.
- Implement access controls: Implement access controls to restrict user access to files and directories based on roles and permissions.
- Use server-side checks: Use server-side checks to verify that any requested file or directory is within the expected range and does not contain any invalid or unexpected characters.
- Monitor and log: Monitor and log all file system access to detect any suspicious activity or attempts to access files or directories outside of the expected range.
By following these steps, you can help prevent Path Traversal vulnerabilities and reduce the risk of unauthorized access to sensitive files and data.
References
Taxonomies
- OWASP Top 10 - A01 Broken Access Control
- CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')
- CWE-23: Relative Path Traversal
- CWE-36: Absolute Path Traversal
Explanation & Prevention
- OWASP: Path Traversal
- OWASP: File Upload Cheat Sheet
- OWASP: Input Validation Cheat Sheet
- CAPEC-126: Path Traversal
- WASC-33: Path Traversal
Related CVEs
Training
Option A: Use send_download securely
Using the :file
option with send_download
with the second parameter coming from user input,
allows attackers to access any file on the system that Elixir has permission to read.
For example:
../../../../../../etc/passwd
If acceptable, the application should generate its own file names and use
those. Otherwise, the provided filename should be properly validated to
ensure it's properly structured, contains no unauthorized path characters
(e.g. /
), and refers to an authorized file.
Generally, it's suggested to avoid allowing users to directly specify the file path send_download
.
Another option is to use the :binary
option, which doesn't treat the second parameter as a
file path, but as the actual content of the file being downloaded, which eliminates the risk
of Path Traversal Attacks.
Solution-specific references:
An example fix is shown below:
Go through the issues that GuardRails identified in the PR/MR.
Locate the following code patterns:
def index(conn, %{"test" => test}) do
send_download conn, {:file, test}
endAnd add security checks to limit what files can be downloaded.
def index(conn, %{"test" => test}) do
# Assume allowed_files is a list of filenames without any directory paths
allowed_files = ["file1.txt", "file2.txt", "report.pdf"]
if test in allowed_files do
base_path = "/path/to/your/downloadable/files"
file_path = Path.join(base_path, test)
if File.exists?(file_path) do
send_download(conn, {:file, file_path})
else
conn
|> put_status(:not_found)
|> text("File not found")
end
else
# If the file is not in the list of allowed files
conn
|> put_status(:forbidden)
|> text("Forbidden")
end
endTest your changes
Ship it 🚢 and relax 🌴
Option B: Use send_file securely
Similar to send_download
, if the third parameter comes from user input,
attackers can access any file on the system that Elixir has permission to read.
For example:
../../../../../../etc/passwd
Generally, it's suggested to avoid allowing users to directly specify the file path send_file
.
Solution-specific references:
An example fix is shown below:
Locate the following code patterns:
def index(conn, %{"test" => test}) do
send_file(conn, 200, test)
endAnd add security checks to limit what files can be downloaded.
def index(conn, %{"test" => test}) do
# Assume allowed_files is a list of filenames without any directory paths
allowed_files = ["file1.txt", "file2.txt", "report.pdf"]
if test in allowed_files do
base_path = "/path/to/your/downloadable/files"
file_path = Path.join(base_path, test)
if File.exists?(file_path) do
send_file(conn, 200, file_path)
else
conn
|> put_status(:not_found)
|> text("File not found")
end
else
# If the file is not in the list of allowed files
conn
|> put_status(:forbidden)
|> text("Forbidden")
end
endTest your changes
Ship it 🚢 and relax 🌴
Option C: Use the File module securely
The Elixir File module exposes several APIs to interact with the file system. If the parameter specifying the file path is controlled by user input, this leads to Path Traversal vulnerabilities.
Solution-specific references:
File.read(dangerous)
File.read!(dangerous)
File.write(dangerous)
File.write!(dangerous)
File.rm(dangerous)
File.rm!(dangerous)
File.rm_rf(dangerous)
File.chmod(dangerous)
File.chmod!(dangerous)
File.chown(dangerous)
File.chown!(dangerous)
File.mkdir(dangerous)
File.mkdir!(dangerous)
File.mkdir_p(dangerous)
File.mkdir_p!(dangerous)
File.cp(dangerous, dangerous2)
File.cp!(dangerous, dangerous2)
File.cp_r(dangerous, dangerous2)
File.cp_r!(dangerous, dangerous2)
File.ln(dangerous, dangerous2)
File.ln!(dangerous, dangerous2)
File.ln_s(dangerous, dangerous2)
File.ln_s!(dangerous, dangerous2)
An example fix is shown below:
Go through the issues that GuardRails identified in the PR/MR.
Locate the following code patterns:
def index(conn, %{"test" => test}) do
File.read(test)
endAnd add security checks to limit what files can be downloaded.
def index(conn, %{"test" => test}) do
allowed_files = ["file1.txt", "file2.txt", "report.pdf"]
if test in allowed_files do
base_path = "/path/to/your/files"
file_path = Path.join(base_path, test)
case File.read(file_path) do
{:ok, contents} ->
# do something with the file contents
{:error, reason} ->
# handle the error, e.g. by sending a 404 response
conn
|> put_status(:not_found)
|> text("File not found")
end
else
# If the file is not in the list of allowed files
conn
|> put_status(:forbidden)
|> text("Forbidden")
end
endShip it 🚢 and relax 🌴
Option D: Migrate to Cloud Storage
Nowadays, there is rarely a need to allow users to interact with local files. A better alternative is storing files in dedicated systems, such as AWS S3. This way attackers can't get access to the local file system.