Skip to main content

Lack of data validation - Path Traversal

Need

Implementation of proper input validation and sanitization to prevent path traversal attacks.

Context

  • Requirement of Node.js v14.0.0 or later for running the application
  • Usage of Express for building web applications and APIs
  • Usage of path for working with file and directory paths

Description

Non compliant code

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.

Steps

  • Validate the input for special elements like '..', '~', and '/'.
  • If the input contains any of these special elements, handle it appropriately (e.g., return an error response).
  • Consider using a whitelist approach to validate the input, allowing only certain characters or patterns.
  • Sanitize the input by removing any special characters or patterns that could be used for path traversal.
  • Ensure that the resolved file path is within the intended target directory.
  • Consider using a different approach to serve files, such as storing them in a database or using a file server.
  • Regularly update and patch the dependencies used in the application to prevent any known vulnerabilities.

Compliant code

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:

  1. The server defines a route for handling GET requests to the /files/:filename endpoint.
  2. The filename parameter is extracted from the request's URL using req.params.filename.
  3. 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.
  4. 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.
  5. 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.
  6. 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.
  7. 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.

References