Skip to main content

Lack of data validation - Path Traversal

Need

Implementation of robust data validation and sanitization mechanisms

Context

  • Usage of C# 7.0 for modern and efficient programming in the .NET ecosystem
  • Usage of Microsoft.AspNetCore.Mvc for building web applications with ASP.NET Core
  • Usage of Microsoft.AspNetCore.Hosting for hosting ASP.NET Core applications
  • Usage of System.IO for input and output operations in .NET development
  • Usage of System.IO.MemoryStream for reading and writing data to memory
  • Usage of System.IO.FileStream for reading and writing files in .NET
  • Usage of System.IO.FileMode for file handling and access control
  • Usage of System.IO.Path for manipulating file and directory paths

Description

Non compliant code

public IActionResult GetFile(string fileName)
{
if (string.IsNullOrEmpty(fileName))
{
return NotFound();
}

var file = Path.Combine(_hostingEnvironment.WebRootPath, fileName);

var fileExists = System.IO.File.Exists(file);
if (!fileExists)
{
return NotFound();
}

var memory = new MemoryStream();
using (var stream = new FileStream(file, FileMode.Open))
{
stream.CopyTo(memory);
}
memory.Position = 0;
return File(memory, GetContentType(file), Path.GetFileName(file));
}

The above code is a simple file retrieval method in an ASP.NET Core Controller. This method takes a filename as a parameter and attempts to open and return this file. The filename is used directly to create a file path which is then opened.

The vulnerability lies in the lack of validation of the fileName parameter. An attacker could provide a fileName that includes path traversal characters (like '../') to move up the directory structure and potentially access sensitive files outside of the intended directory.

For example, if an attacker passed in fileName as ../../appsettings.json, it would lead to the application returning the contents of the appsettings.json file which could contain sensitive application configuration data like database connection strings, API keys, etc. This is a classic example of a Path Traversal vulnerability, where an attacker can manipulate the file path to access files that should not be accessible.

Steps

  • Implement input validation to ensure that the 'fileName' parameter is a valid file name.
  • Use a whitelist approach to validate the 'fileName' parameter, allowing only specific characters or patterns.
  • Avoid using user-provided input directly in constructing file paths.
  • Consider using a secure file storage mechanism that does not rely on user-provided file names.
  • Implement proper error handling and return appropriate error responses when a file is not found or an error occurs.

Compliant code

public IActionResult GetFile(string fileName)
{
if (string.IsNullOrEmpty(fileName) || !IsValidFileName(fileName))
{
return NotFound();
}

var file = Path.Combine(_hostingEnvironment.WebRootPath, fileName);

var fileExists = System.IO.File.Exists(file);
if (!fileExists)
{
return NotFound();
}

var memory = new MemoryStream();
using (var stream = new FileStream(file, FileMode.Open))
{
stream.CopyTo(memory);
}
memory.Position = 0;
return File(memory, GetContentType(file), Path.GetFileName(file));
}

private bool IsValidFileName(string fileName)
{
// Define a whitelist of valid characters.
var validChars = new Regex("^[a-zA-Z0-9_\\\\.-]+$");

// Check if the file name is valid.
return validChars.IsMatch(fileName);
}

The updated code includes a new method IsValidFileName(string fileName). This method uses a regular expression to validate the fileName parameter. The regular expression ^[a-zA-Z0-9_\\\\.-]+$ allows only alphanumeric characters, underscores, dots, and hyphens in the file name. This is a whitelist approach to input validation, which is generally more secure than a blacklist approach.

The GetFile(string fileName) method now calls IsValidFileName(string fileName) to validate the fileName parameter. If the fileName parameter is null, empty, or invalid, the method returns a NotFound result.

This approach prevents path traversal attacks by ensuring that the fileName parameter does not contain any special characters that could be used to navigate to other directories. It also avoids using user-provided input directly in constructing file paths, which is a common source of security vulnerabilities.

In addition, the code continues to check if the file exists and returns a NotFound result if it does not. This provides proper error handling and appropriate error responses.

References