Implementation of proper input validation and sanitization to prevent path traversal attacks.
import express from 'express';
import path from 'path';
const app = express();
app.get('/files/:filename', (req, res) => {
const filename = req.params.filename;
const filePath = path.join(__dirname, 'uploads', filename);
res.sendFile(filePath);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
The vulnerability in this code is a lack of data validation, specifically a Path Traversal vulnerability. This vulnerability occurs when the software uses external input (in this case, the
filename
parameter from the request URL) to construct a pathname without properly validating or neutralizing special elements within the pathname.
In this code, the
filename
parameter is directly used to construct the
filePath
by joining it with the
uploads
directory using the
path.join()
function. However, there is no validation or neutralization of special elements like
..
,
~
, or
/.
in the
filename
parameter.
This means that an attacker can potentially manipulate the
filename
parameter to traverse to a location outside of the intended target directory (
uploads
). For example, an attacker could provide a
filename
value of
../../../../../etc/passwd
, which would resolve to the path
/etc/passwd
, a sensitive system file.
This vulnerability can have serious consequences as it allows an attacker to access sensitive files or directories on the server, potentially leading to unauthorized access, information disclosure, or even remote code execution.
To mitigate this vulnerability, it is recommended to validate and neutralize the input for special elements like
..
,
~
, or
/.
. This can be done by implementing proper input validation and sanitization techniques to ensure that the
filename
parameter only contains valid characters and does not contain any special elements that could lead to path traversal.
import express from 'express';
import path from 'path';
const app = express();
app.get('/files/:filename', (req, res) => {
const filename = req.params.filename;
// Validate the input for special elements
if (filename.includes('..') || filename.includes('~') || filename.includes('/')) {
return res.status(400).send('Invalid filename');
}
// Sanitize the input by removing any special characters or patterns
const sanitizedFilename = filename.replace(/[^\\w\\s.-]/gi, '');
const filePath = path.join(__dirname, 'uploads', sanitizedFilename);
// Ensure that the resolved file path is within the intended target directory
const resolvedPath = path.resolve(filePath);
if (!resolvedPath.startsWith(path.join(__dirname, 'uploads'))) {
return res.status(400).send('Invalid filename');
}
res.sendFile(resolvedPath);
});
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
The fixed code addresses the vulnerability by implementing data validation and neutralization techniques to prevent path traversal attacks.
1. The code uses the Express framework to handle HTTP requests.
2. It imports the necessary modules, including
express
and
path
.
3. The server listens on port 3000.
In the vulnerable code, the filename parameter from the request was directly used to construct a file path without any validation or neutralization. In the fixed code:
4. The server defines a route for handling GET requests to the
/files/:filename
endpoint.
5. The filename parameter is extracted from the request's URL using
req.params.filename
.
6. The code then validates the filename for the presence of special elements like
..
,
~
, or
/
using the
includes
method. If any of these special elements are found, a 400 Bad Request response is sent, indicating an invalid filename.
7. To further neutralize the input, the code uses a regular expression to remove any characters that are not alphanumeric, whitespace, period, or hyphen from the filename. This helps prevent any potential injection of special characters or patterns.
8. The sanitized filename is then used to construct the file path using the
path.join
method, ensuring that the path is relative to the
uploads
directory.
9. To validate that the resolved file path is within the intended target directory, the code uses
path.resolve
to get the absolute path and checks if it starts with the expected path to the
uploads
directory. If the resolved path is outside the intended target, a 400 Bad Request response is sent.
10. Finally, if the file path is valid, the code sends the file using
res.sendFile
, which safely serves the file to the client.
By implementing these measures, the fixed code prevents an attacker from constructing a malicious pathname and ensures that the resolved file path remains within the intended target directory.