Jump to content

Read/download a file from a non web-accessible folder


sparky753

Recommended Posts

WATCH OUT! The sample script is VERY VERY VERY VERY insecure.

 

DON'T USE

 

<?php

$filename = $_GET['ID'];
$fileloc = "/PATH/OUTSIDE/WEBROOT/" . $filename; // example path would be something like "/home/USERNAME/"

header("Content-type: application/force-download");
header("Content-Disposition: attachment; filename=" . $filename);
@readfile ($fileloc);

?>

 

You're allowing the end user to enter ANYTHING they want... for example, accessing the page with the query ?ID='../../some/other/path' will allow the user to download pretty much anything PHP has access to.

 

Using str_replace to remove '/' or even better, using regex to provide a whitelist of characters will provide a much more secure method of retrieving the file. You may also want to check if the file exists ( file_exists() ) before attempting to serve it.

 

You may also run into memory issues here, as PHP load the file into memory... for that, here's a function grabbed from the PHP user contrib that allows you to read a file piece by piece and serve it to the user. I use this to serve 500mb files, while only using about 500kb of memory at any given time.

 

<?php
function readfile_chunked($filename,$retbytes=true) {
   $chunksize = 1*(1024*1024); // how many bytes per chunk
   $buffer = '';
   $cnt =0;
   // $handle = fopen($filename, 'rb');
   $handle = fopen($filename, 'rb');
   if ($handle === false) {
       return false;
   }
   while (!feof($handle)) {
       $buffer = fread($handle, $chunksize);
       echo $buffer;
       ob_flush();
       flush();
       if ($retbytes) {
           $cnt += strlen($buffer);
       }
   }
       $status = fclose($handle);
   if ($retbytes && $status) {
       return $cnt; // return num. bytes delivered like readfile() does.
   }
   return $status;

}
?> 

Discomatt is correct though on the security side of that script.

as using GET isn't the most secure way of doing things. You should also add in some validation.

 

At the same time if you hard code your path, as in my example, the user can not do as suggested since trying to add some other path would in actuality concatenate to the end of the hard coded one.

 

 

 

At the same time if you hard code your path, as in my example, the user can not do as suggested since trying to add some other path would in actuality concatenate to the end of the hard coded one.

 

 

Hard coded path is obvious, but most servers will convert relative paths to absolute... so, let's say $filename is = '../somefile.ext';

 

$fileloc = "/PATH/OUTSIDE/WEBROOT/" . $filename;

 

Would resolve as '/PATH/OUTSIDE/somefile.php' on most servers. Not something you want.

good point.

 

ideally i would store file names "with path" in a DB personally.

then retrieve the file path and name from the DB and send that through a session variable to the download script.

another benefit of this method is you can add a user log in and set permissions as to access rights.

 

otherwise another non DB idea would be to make your links actually be buttons inside their own forms.

Then you can use POST data to send the information to the downloader script. which is a lot more secure.

 

I do like your idea for sending parts of the file to prevent running out of cache on the server. That will cut down on a lot of unnecessary load.

 

 

otherwise another non DB idea would be to make your links actually be buttons inside their own forms.

Then you can use POST data to send the information to the downloader script. which is a lot more secure.

 

This is a common misconception, as POST variables are extremely easy to edit with available tools. Firefox has an add-on known as 'Tamper Data' that does specifically this. Your idea of database storage is ideal...

 

A database-less way would be to have an array of 'allowed file names', and check the user input against it using in_array()

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.