php.htaccessopen-basedir

Routing *.zip file requests one level above, with open_basedir in effect


Is it possible to use .htaccess to route zip file requests to the level of public_html, if open_basedir is in effect?

The reason I'm asking is because I'm trying to force a download of any of the zip files which are on the same level as public_html / www, if a visitor clicks on a seemingly direct download link.

An extremely simplified code for what I'm trying to make could look something like this.

.htaccess

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule (.*)\.(zip)$ dl.php?filename=$1 [L,QSA]

index.php

//various bits of php code, and this below
<div>
    <a href="1.zip">1</a>
</div>
<div>
    <a href="2.zip">2</a>
</div>
<div>
    <a href="3.zip">3</a>
</div>

dl.php

$file = "../../".$_GET['filename'].".zip";

header('Content-type: application/zip');
header('Content-length: ' . filesize($file));
readfile($file);

die();

Should I even be doing it like this, or is there a better way? I'm just trying to do this for one specific filetype, available on one specific page (index.php, in the example), so I'm not really looking to make an entire routing system just for this.


Solution

  • Assuming that your .htaccess works properly then you would need dl.php to do something like this in order to be secure:

    $directory = '../../';
    
    // Get the a file listing from the directory which contains the files
    foreach(array_diff(scandir($directory), array('..', '.')) as $file)
    {
        if(
            strtolower($file) === strtolower($_GET['filename'].'.zip') && // Check each filename against the requested filename in $_GET. Use strtolower() for case-insensitive filesystems
            is_file($directory.$file) // Make sure that this is a file and not a folder
        )
        {
            // Serve it up!
            header('Content-type: application/zip');
            header('Content-length: ' . filesize($directory.$file));
            readfile($directory.$file);
            die();
        }
    }