views:

21

answers:

3

I'm looking for a way to filter and control access to a folder on a website.

However, what I'd like to do is filter the access requests via PHP and Apache, a little like the way filters are implemented in Java.

So, when a user tries to visit

http://www.mywebsite.example/files/afile.zip

I'd like it first to pass through a PHP script to ensure that the user is not trying to download the same file multiple times, or is doing something else that could break the website.

Is something like this possible? I know it's possible to simply move files above the document root and then serve them that way, but I was looking for something a little more elegant.

Can I use any combination of .htaccess and mod_rewrite to do this?

A: 

I think moving the files above the web root is more elegant than hacking around with around mod_rewrite. Just saying. They're either directly served and belong in the web root, or they're aren't and therefore don't.

Regardless, it's doable fairly easily, in files/.htaccess

RewriteEngine On
RewriteRule . validate.php?file=$0

validate.php should now have access to the request file name in $_GET['file'].

(Code untested; let me know if you get errors. mod_rewrite never really liked me all that much.)

Matchu
What if I typed the following: `files/validate.php` in a browser? Would that create infinite loop of DOOM?
Marco Ceppi
@Marco Ceppi - if there were no safety catch, `validate.php` would probably be served as a download. The redirect only applies if we're actually redirecting to the file to download, which I don't think is the case.
Matchu
@Marco Ceppi - oh, I get it now. Don't use `include` or `require` for things like this. Use `file_get_contents` and serve up the raw data. Otherwise, you hit security issues involving PHP code hiding in EXIF data, inevitable errors when a binary file ends up containing `<?` in it somewhere, etc.
Matchu
A: 

You can add something like this into .htaccess:

Options +FollowSymLinks
RewriteEngine On
RewriteBase /
RewriteRule ^/files/(.+)\.zip$ path/to/phpscript.php?file=$1.zip [L,NC,QSA]

This will redirect all requests for a file to that script. You can perform logic checking against any user data and then set the header Content-Type, file name, and read the stream of the file into the browser.

I need to mention that the path (in this case "/files/" shouldn't exist. You may want to actually keep the files out of the public web and create links to all the files pointed to /files/NAME.EXT

Lastly that code will only work for ZIP you can do the following two examples. First will will only run against a series of file extensions. Second will take any file after the /files/ path.

Options +FollowSymLinks
RewriteEngine On
RewriteBase /
RewriteRule ^/files/(.+)\.(zip|pdf|exe|msi)$ path/to/phpscript.php?file=$1.$2 [L,NC,QSA]

Second:

Options +FollowSymLinks
RewriteEngine On
RewriteBase /
RewriteRule ^/files/(.*)$ path/to/phpscript.php?file=$1 [L,NC,QSA]

Then you would need to create a script "phpscript.php" that follows:

<?php

$file = $_GET['file'];
$path = "secret/non-webpublic/path/";
if( is_file($path . $file) )
{
    //Check if USER can view file
}
else
{
    header("HTTP/1.0 404 Not Found");
    die();
}

?>

Something like that.

Marco Ceppi
A: 

I would use mod_rewrite for this. Ive done something like this in the past where users were able to download videos but had to ensure they were logged in. Went something like this:

href="/video/download/path-to-file.ext"

in my .htaccess

RewriteEngine on
RewriteRule ^video/download/(.*)?$ video-download.php?path=$1 [QSA,L]

in my video-download.php I did a check to see if they were logged in, tracked who download the video etc, etc before throwing them the download dialog box.

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Length: ' . filesize($filename_location));
header('Content-Disposition: attachment; filename=' . basename($filename));
readfile($filename_location);

In your case, soon as they hit this page you want to add a counter against their session and base your logic around that.

PHPology