File access control

… and bandwidth throttling, using PHP.

If you’re in a situation where you need to control who has access to certain files/downloads, PHP can do it all for you. Even better, you can use the same script to throttle how quickly data is sent to the user. Let’s take a look at this problem:

First, you obviously need some kind of files that need protection. Make a downloads directory and stick a .htaccess file in there with the following contents:
Options -Indexes
Order Deny, Allow
Deny from all
Allow from none

Of course you’ll want to also put a file worth downloading: topsecretdownload.pdf

Create a file in your web root called download.php. In it you’re going to put some fairly straightforward code to send a file to only authorized users. There are a few things you should take care of yourself, such as proper escaping of user-supplied data and user validation (they’re beyond the scope of this post).

//clean input so you don't have any nasties.
//You'll have to provide a similar function yourself
$fname=cleaninput($file);
$path='downloads/'.$fname;

//verify the user
$valid_user=is_valid_user($userid);

if($valid_user && is_file($path))
{
   //sends the appropriate file header, sends file contents
   header=('Content-type: application/pdf');
   readfile($path);
}

And there you have it! The files are safe from snooping via requests directly to Apache, and you can even make sure only the right people can download topsecretfile.pdf

If you want to throttle download for some reason, replace the readfile($file); line with the following code:

    $handle=fopen($file,'rb');

    //number of kilobytes per second you want to
   //throttle the file download to.
    $rate = 20; 
    while(!feof($handle) && (connnection_status()==0))
      {
        //tells the script to sleep for one second
        sleep(1);

        //reads 1024*$rate bytes at a time
        print(fread($handle, 1024*$rate);

        //send the output buffer
        flush();
      }

So, in this example, we don’t want any users to download faster than 20 kilobytes per second. sleep(1) basically makes sure the while() loop doesn’t execute faster than 1 time per second, though sleep() can be used in many other situations besides this.

One thing to point out: if you’re checking user authorization against a database, you probably want to close the db connection before you start sending the file over. This is because PHP will keep a connection to the DB open as long as the script is running, or until you explicitly close the connection before the end of the script. This is especially important as site traffic increases (there’s a chance of hitting the max allowed connections to the database).

Finally, you can always use mod_rewrite to make nicer URLs: http://www.domain.ext/files/topsecretfile.pdf instead of http://www.domain.ext/download.php?file=topsecretfile.pdf. I’ll leave that to another quick totorial.

Enjoy!

edit: I just realized WordPress removed part of my code sample. Everything should be working fine now.

Leave a comment

Hey there! Come check out all-new content at my new mistercameron.com!