8

I am developing a system which allows registered users (who could be anybody) to upload files. I've block mime-types etc. to attempt to restrict the files to .doc, .docx and .pdf types, but for additional security, they are uploaded to a folder outside the webroot.

Other users can then choose to download the files. How do I allow them to do that? Obviously I can't just put in a link to the file, as it's outside the webroot. I'm not sure how to reach the file though! I presume I can use the php file functions to get to the file, but how do I then 'serve it up' to the user who has requested it?

What security implications might all of this have?

Thanks.

Sharon
  • 111
  • 1
  • 1
  • 6
  • possible duplicate of [Allow users to download files outside webroot](http://stackoverflow.com/questions/3884677/allow-users-to-download-files-outside-webroot) – Gordon Dec 03 '10 at 12:06
  • you know that this is really quite dangerous? You will notice that sites use a different domain name for user uploaded content. – Tom Hawtin - tackline Dec 03 '10 at 12:10
  • 2
    @Tom unless you point out why it is dangerous, it is not dangerous. – Gordon Dec 03 '10 at 12:11
  • Hi Gordon, I'd seen that thread, but it didn't seem to be asking what I need - it seemed to be wanting to display the result in the browser. – Sharon Dec 03 '10 at 12:18
  • @Sharon Pekka'as answer to that question solves your problem of how to provide download links to files outside the publicly accessible portion of your webserver. – Gordon Dec 03 '10 at 12:31
  • @Gordon I consider that attitude to be extremely dangerous. – Tom Hawtin - tackline Dec 03 '10 at 12:32
  • 2
    @Tom That's as meaningful as your previous comment. Unless you specify what you are talking about, it's just vague. Explain why you think it is dangerous please. Give proof. – Gordon Dec 03 '10 at 12:37
  • 2
    @Tom More explanation would be good; are you suggesting that user uploaded items should be served from a different domain? Why? – El Yobo Dec 03 '10 at 13:04

4 Answers4

10

You need a PHP script that does the following:

  1. Set the content-type header correctly (depending on what the user is downloading)
  2. Set the content-length header correctly (depending on the file size)
  3. Open the file for reading (you can use fopen)
  4. Read the file and output its content to the output stream
  5. Done

You can also use readfile function to do basically the same. Here's an example from PHP's site:

<?php
$file = 'monkey.gif';

if (file_exists($file)) {
    header('Content-Description: File Transfer');
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename='.basename($file));
    header('Content-Transfer-Encoding: binary');
    header('Expires: 0');
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
    header('Pragma: public');
    header('Content-Length: ' . filesize($file));
    ob_clean();
    flush();
    readfile($file);
    exit;
}
?>
Pablo Santa Cruz
  • 176,835
  • 32
  • 241
  • 292
  • 1
    Slightly more complete example here, including mime type and possibly more correct casing - http://stackoverflow.com/questions/4275830/4277145#4277145; Gordon, what's missing? – El Yobo Dec 03 '10 at 13:02
0

See the answers to this similar question: Refer to a file outside the website tree for downloading purposes, which links to the PHP header function manual page.

Dave Everitt
  • 17,193
  • 6
  • 67
  • 97
  • 1
    While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – Musa Nov 16 '12 at 02:12
  • Point taken - at present I only have time to add a little more info about the linked resources, so for now that's what I've done. – Dave Everitt Dec 24 '12 at 12:43
0

You can put your files directory in root and apply mod rewrite rules to secure and show a virtual path to the users instead of real path.

Adeel
  • 605
  • 1
  • 6
  • 16
  • Can you tell me more about this? The directory is in /userfiles from the home directory. What rules should I apply? How do I show a virtual path? Thanks. – Sharon Dec 03 '10 at 12:19
  • You can edit your .htaccess file and define a rule like RewriteRule ^download.php$ download.php?path=yourpath/$1.doc Your user can only see download.php – Adeel Dec 03 '10 at 12:35
  • That pretty much defeats the security benefits of putting it outside the document root in the first place. – El Yobo Dec 03 '10 at 12:58
0

Try the following:

$fileName = basename($_GET['file']);
$path = 'path/to/data/'.$fileName;

// define $mimeType and $isAuthenticated

if ($isAuthenticated && file_exists($path)) {
    // serve file
    header('Content-type: '.$mimeType);
    header('Content-Disposition: attachment; filename="'.$fileName.'"');
    readfile($path);
} else {
    // 404
}

This will probably need some more headers to suit your needs, but you should get an idea how this can be used.

jwueller
  • 30,582
  • 4
  • 66
  • 70