Secure File Download Workflow

Would be possible for the BF Panel to have customizations where users could create files on the server, but the file links would be hidden in the DOM?

Maybe have a key:value system where a user requests a file to download and the request is passed back to the server and then the users is served the file?

I am using SureCart’s protected file system for some projects, but that plugin is more than smaller sites need.

Thanks for the consideration.

@Daniele

ChatGPT and I built this today for my own use case and it works fine, but:

  1. I’m not a security expert and i don’t want to introduce any vulnerabilities - i think i did an ok job mitigating that.
  2. its hard to manage this in the functions.php
  3. there has to be a better way :slight_smile:

but here is my thought process around the feature request.

function handle_file_download() {

if (isset($_GET['fileKey'])) {
    // Initialize the uploads directory and ensure the session is secure.
    $uploadDirInfo = wp_upload_dir();
    $baseDir = $uploadDirInfo['basedir'];

    // Define a mapping of keys to files. The actual mapping should be secure and not guessable.
    $fileMapping = array(
        'hfdiwekxnmsuwlrlxkjhsm' => '/2024/02/Test.txt',
    );

    // Sanitize the fileKey to avoid directory traversal or other injection attacks.
    $fileKey = preg_replace('/[^-a-zA-Z0-9_]/', '', $_GET['fileKey']);

    // Immediately terminate if the fileKey is not valid or does not exist in the mapping array.
    if (!array_key_exists($fileKey, $fileMapping)) {
        die('Error: Unauthorized file access.');
    }

    $filePath = $baseDir . $fileMapping[$fileKey];
    $resolvedPath = realpath($filePath);

    // Verify the file exists, is not a directory, is within the base directory, and is a regular file.
    if (!$resolvedPath || !file_exists($resolvedPath) || is_dir($resolvedPath) || strpos($resolvedPath, $baseDir) !== 0 || !is_file($resolvedPath)) {
        die('Error: Invalid file request.');
    }

    $fileName = basename($resolvedPath);

    // Set headers to serve the file download.
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . htmlspecialchars($fileName, ENT_QUOTES, 'UTF-8') . '"');
    header('Expires: 0');
    header('Cache-Control: must-revalidate');
    header('Pragma: public');
    header('Content-Length: ' . filesize($resolvedPath));

    // Clear the output buffer and read the file to serve it.
    ob_clean();
    flush();
    readfile($resolvedPath);
    exit;
}

}

add_action(‘init’, ‘handle_file_download’);