views:

52

answers:

3

I need to serve up large files (> 2gb) from an Apache web server. The files are protected downloads, so I need some kind of way to authorize the user. The CMS I'm using uses cookies checked against a MySQL database to verify the user. On the server, I have no control over max_execution_time, and limited control over memory_limit.

My technique has been working for small files. After the user has been authorized in PHP (by the CMS), I use readfile() to serve the file, which is stored above the document root to prevent direct access. I've read about techniques to chunk the download or to use fpassthru to get around the PHP memory limit. But I haven't found a technique to get around the max_execution_time limit.

I thought about storing the file within the document root, so we could bypass PHP entirely. But what I can't figure out is how to restrict access with htaccess. I need to verify the user against the database before I can serve them the file.

Thanks.

A: 

What about using symlinks? If you have a folder example:

userfacingfiles/
  md5_of_order_id1 --> protected-file.exe
  md5_of_order_id2 --> protected-file.exe

protectedfiles/
  .htaccess (contains deny from all)
  protected-file.exe

Basic Example:

$salt = 'canttouchthis';

function create_symlink($order_id, $salt, $protected_file) 
{
  $info = pathinfo('protectedfiles/'.$protected_file);

  symlink('protectedfiles/'.$protected_file, 'userfacingfiles/'.md5($order_id.$salt).'.'.$info['extension']);
}


function get_file($order_id, $salt, $extension)
{

  header('Location: userfacingfiles/'.md5($order_id.$salt).'.'.$extension);
  exit();
}

usage:

When the user pays:

create_symlink(1, 'secureSALT', 'ebook.pdf');

When the user wants to download their ebook

get_file(1, 'secureSALT');

This may not be the most portable method, but because you're redirecting the user the web server is handling the downloads.

Kieran Allen
that was just painful to read
Joe Hopfgartner
+1  A: 

The nicest solution in my opinion: install mod_xsendfile in your Apache, have the PHP script authorize the user, and on success send a response with an X-Sendfile header pointing to the location of the protected file. From that point on, Apache does the work of serving the file to the client; not PHP.

hobbs
+1 for `mod_xsendfile`. Don't do this work in PHP. Apache (or any production quality web server, for that matter) is optimized to serve files. Let it do its job and get your script interpreter out of the way. Lighttpd supports the same header and Nginx does the same thing, but with `X-Accel-Redirect`.
Steve Madsen
mod_xsendfile is the **better** solution. However, if this isn't available to you (shared hosting/static build) then i recommend using Symlinks or Hardlinks (as mentioned below) as with that you can still pass the serving of the download to the web server.
Kieran Allen
Links are security through obscurity. Nothing stops an end user from sharing those links. That's fine in some cases, but let's be honest about the trade-offs.
Steve Madsen
good point - didn't think about that. +1
Kieran Allen