Insecure File Management
Fixing Path Traversals
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
Path Traversal (write operations)
This vulnerability allows untrusted input to define or influence a file system path used in one of the many write, or otherwise destructive methods in the official Nodejs file system package.
This allows one of the many write, or otherwise destructive fs methods, given a file or directory path from an attacker to execute.
If accepting untrusted data as a file or directory path to be used as input for one of the many Nodejs write fs methods, it should be properly validated, filtered, and sanitized.
Note: Due to the complexity of this vulnerability, this rule currently does not detect secure implementations. Please mark issues as 'false positives', or 'fixed' in the GuardRails dashboard.
Option A: Provide allow list for untrusted paths
Maintain an allow list of paths to compare the untrusted path with, and only allow the path to progress through execution flow if it matches an item in the allow list.
By defining allow lists for validation, you will effectively filter out path traversal characters in their many forms.
Locate a vulnerable pattern similar to the following:
import express from 'express';
import { writeFile } from 'node:fs/promises'
const app = express();
app.get('/users', async (req, res) => {
await writeFile(req.body.path, req.body.text { encoding: 'utf8' })
.then((resolved) => {
!resolved && res.send(`${req.body.path} was successfully written to.`);
})
.catch((err) => {
res.status(500).send({ error: 'An error occurred' });
})
});
app.listen(3000, function() {
console.log('Listening on port 3000');
});Replace the vulnerable code with something similar to the following:
Validation on the client side is also a good idea for user experience so that the user knows what a valid file name is before it is sent to the server.
You need to define acceptable and agreed on file extensions, file name characters, file name length. Restrict characters to a business agreed allowed subset specifically, such as alphanumeric characters, hyphens, spaces, and periods.
In this way, URL-encoding, traversal character sequences, and many other undesirable file names will be filtered out.
import express from 'express';
import { writeFile } from 'node:fs/promises'
const app = express();
app.get('/users', async (req, res) => {
// Perform any required decoding (such as percent decoding).
// Then create your validation routine.
const validatedPath = validateFilterSanitizePath(req.body.fileName);
// It's also simpler if you can define the directory in code,
// then you only need to validate the file name.
const fileAllowList = {
'how-to-programme-in-c': '/media/books/cProgramming',
'how-to-programme-in-c++': '/media/books/cPPProgramming',
'how-to-programme-in-python': '/media/books/pythonProgramming',
// ...: '...'
};
const path = fileAllowList[validatedPath];
// Just be aware that accepting untrusted data to write to a file will need
// careful consideration. Is there any way this file can be executed?
// If so, you will need to also validate, filter and sanitize the intended content.
await writeFile(path, req.body.text, { encoding: 'utf8' })
.then((resolved) => {
!resolved && res.send(`${path} was successfully written to.`);
})
.catch((err) => {
res.status(500).send({ error: 'An error occurred' });
})
});
app.listen(3000, function() {
console.log('Listening on port 3000');
});Test it
Ship it 🚢 and relax 🌴
In regards to defense-in-depth, make sure that the file and directory permissions are only as permissive as they need to be, and no more.
Path Traversal (read operations)
This vulnerability allows untrusted input to define or influence a file system path used in one of the many read, or otherwise non-destructive methods in the official Node.js file system package.
This allows one of the many read, or otherwise non-destructive fs methods, given a file or directory path from an attacker to execute.
If accepting untrusted data as a file or directory path to be used as input for one of the many Node.js read fs methods, it should be properly validated, filtered, and sanitized.
Note: Due to the complexity of this vulnerability, this rule currently does not detect secure implementations. Please mark issues as 'false positives', or 'fixed' in the GuardRails dashboard.
Option A: Provide allow list for untrusted paths
Maintain an allow list of paths to compare the untrusted path with, and only allow the path to progress through execution flow if it matches an item in the allow list.
By defining allow lists for validation, you will effectively filter out path traversal characters in their many forms.
Locate a vulnerable pattern similar to the following:
import express from 'express';
import { readFile } from 'node:fs/promises'
const app = express();
app.get('/users', async (req, res) => {
await readFile(req.body.path, { encoding: 'utf8' })
.then((fileContents) => {
res.send(fileContents);
})
.catch((err) => {
res.status(500).send({ error: 'An error occurred' });
})
});
app.listen(3000, function() {
console.log('Listening on port 3000');
});Replace the vulnerable code with something similar to the following:
Validation on the client side is also a good idea for user experience so that the user knows what a valid file name is before it is sent to the server.
You need to define acceptable and agreed on file extensions, file name characters, file name length. Restrict characters to a business agreed allowed subset specifically, such as alphanumeric characters, hyphens, spaces, and periods.
In this way, URL-encoding, traversal character sequences, and many other undesirable file names will be filtered out.
import express from 'express';
import { readFile } from 'node:fs/promises'
const app = express();
app.get('/users', async (req, res) => {
// Perform any required decoding (such as percent decoding).
// Then create your validation routine.
const validatedPath = validateFilterSanitizePath(req.body.fileName);
// It's also simpler if you can define the directory in code,
// then you only need to validate the file name.
const fileAllowList = {
'how-to-programme-in-c': '/media/books/cProgramming',
'how-to-programme-in-c++': '/media/books/cPPProgramming',
'how-to-programme-in-python': '/media/books/pythonProgramming',
// ...: '...'
};
await readFile(fileAllowList[validatedPath], { encoding: 'utf8' })
.then((fileContents) => {
res.send(fileContents);
})
.catch((err) => {
res.status(500).send({ error: 'An error occurred' });
})
});
app.listen(3000, function() {
console.log('Listening on port 3000');
});Test it
Ship it 🚢 and relax 🌴
In regards to defense-in-depth, make sure that the file and directory permissions are only as permissive as they need to be, and no more.