logo

External control of file name or path - Dart


Need

Secure handling of file uploads


Context

  1. Usage of Dart 2.0 for building high-performance, cross-platform applications
  2. Usage of Shelf for managing and organizing data in a Node.js application
  3. Usage of shelf_io for managing input/output operations in a Shelf application

Description

Insecure Code Example

import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;

void main() async {
  var server = await io.serve(handler, 'localhost', 8080);

  print('Serving at http://${server.address.host}:${server.port}');
}

shelf.Response handler(shelf.Request request) {
  if (request.url.path == 'upload') {
    var filePath = request.headers['filePath'];
    var fileContent = request.headers['fileContent'];

    var file = File(filePath);
    file.writeAsStringSync(fileContent);

    return shelf.Response.ok('File uploaded successfully');
  }

  return shelf.Response.notFound('Not Found');
}

In the above code, we have a simple HTTP server built using the `shelf` framework. The server listens for a request at the `/upload` endpoint and then takes the `filePath` and `fileContent` from the headers of the request. The `filePath` is used to create a new file on the server's filesystem, and the `fileContent` is written to that file. The vulnerability here lies in the fact that the server trusts the `filePath` header from the request. An attacker could manipulate the `filePath` to overwrite any file on the server's filesystem, or to write new files in sensitive locations. For example, an attacker could set `filePath` to `/etc/passwd` to overwrite the server's password file, or to `/var/www/html/evil.html` to serve malicious content from the server's web root. This is a classic example of the 'External Control of File Name or Path' vulnerability, where the application fails to properly validate input that is used to construct file names or paths.

Steps

  1. Validate the file path provided in the request to ensure it is within the expected directory.
  2. Do not allow absolute paths or paths that contain '..' to prevent directory traversal attacks.
  3. Consider using a library or built-in function to safely join paths.
  4. Generate a server-defined filename instead of using the one provided in the request.
  5. Ensure that the application has the least privilege on the file system, especially on the directories where the files are stored.

Secure Code Example

import 'dart:io';
import 'package:path/path.dart' as path;
import 'package:shelf/shelf.dart' as shelf;
import 'package:shelf/shelf_io.dart' as io;

void main() async {
  var server = await io.serve(handler, 'localhost', 8080);

  print('Serving at http://${server.address.host}:${server.port}');
}

shelf.Response handler(shelf.Request request) {
  if (request.url.path == 'upload') {
    var filePath = request.headers['filePath'];
    var fileContent = request.headers['fileContent'];

    // Validate the file path
    if (filePath.contains('..') || path.isAbsolute(filePath)) {
      return shelf.Response.forbidden('Invalid file path');
    }

    // Safely join paths
    var safeFilePath = path.join('/expected/directory', filePath);

    // Generate a server-defined filename
    var fileName = path.basename(safeFilePath);
    var serverDefinedFilePath = path.join('/expected/directory', fileName);

    var file = File(serverDefinedFilePath);
    file.writeAsStringSync(fileContent);

    return shelf.Response.ok('File uploaded successfully');
  }

  return shelf.Response.notFound('Not Found');
}

The updated code includes several changes to address the vulnerability: 1. **File Path Validation**: The code now checks if the provided file path contains '..' or is an absolute path. If either condition is true, the server responds with a 'Forbidden' status, preventing potential directory traversal attacks. 2. **Safe Path Joining**: The `path.join` function from the `path` package is used to safely join the expected directory with the provided file path. This ensures that the file is always saved within the expected directory, even if the provided file path tries to navigate to a different directory. 3. **Server-Defined Filename**: Instead of using the filename provided in the request, the server now generates its own filename using the `path.basename` function. This prevents an attacker from controlling the file name or path. 4. **Least Privilege**: Although not shown in the code, it is recommended to ensure that the application has the least privilege on the file system, especially on the directories where the files are stored. This can be done through proper configuration of the server and file system permissions.


References

  • 098 - External control of file name or path

  • Last updated

    2023/09/18