Allow users to download files outside webroot
Use
a symlink pointing to
/var/uploads
(tutorial here)a Apache
Alias
directiveAlias /uploads /var/uploads
(must be in httpd.conf)or a proxy PHP script that accepts a GET variable
filename=upload.jpg
and fetches the file e.g. usingfpassthru()
the latter is the least preferable option because it is resource intensive, but sometimes it's the only alternative. It also needs proper securing to prevent an attacker from getting other files on your server through the proxy.
Downloading files outside the webroot
After much trial and error, I appear to have solved the problem.
The issue was in using jQuery/Ajax.
When I changed the way the downloadscript.php
file is accessed to a direct $_GET
request from the link on the page, it worked a treat.
Anyway, thanks for your help everyone!
Chris
File Uploads and Downloads when files are stored outside of the webroot? (PHP)
Concerning (1):
Theory is: You store the file somewhere outside the webroot to prevent direct access and to prevent execution on the server side. You have to be able to find it, when the user provides the correct parameters. (However, you should be careful, how do the files get presented to the user? A database could be helpful here) If security is a concern, you have to make sure that a user cannot access files that he should not have access to (when he has the right download link but doesn't have permissions for example, because so far your download links don't seem to be very cryptic).
The script you mentioned in your question is quite alright, although I'd probably just use fpassthru
instead of the feof-fread-echo-loop. The idea here is essentially to figure out the mime type, add that to the headers and then dump the contents into the output stream.
Concerning (2):
Using a database especially with prepared statements is quite safe and provides some extra possibilities. (Like adding some comment to the attachment, a timestamp, filesize, reordering, ...)
You don't check the upload_name
, this could very well be ../../your webroot/index.php
or something similar. My advice would be to store the files that are uploaded as something unimaginative as "file_ID" and store that id with the original filename in the database. You should probably also remove any leading multiple dots, slashes ("directory") and similar.
Loading bars ... well that's taste I guess.
Upload files outside of webroot
you can upload where ever you want using the move_uploaded_file function, just make sure the webserver can write in the destination directory.
After you have to create a script that would read the file and pass it to the browser so you can make sure user have paid the file.
exemple
<?php
// insert your logic here to verify the user has access to this file.
// open the file in a binary mode
$name = 'yourfile';
$fp = fopen($name, 'rb');
// send the right headers
header("Content-Type: image/png");
header("Content-Length: " . filesize($name));
// dump the picture and stop the script
fpassthru($fp);
exit;
?>
You have to be careful about the content-type also make sure the user cannot every file of your server if you use a $_GET variable for getting the filename.
Using php and jQuery to allow authenticated users to download a file outside apache's webroot directory
OK, after some more research and a lot of trial and error I got this to work, the code now looks like this:
Client Side (jQuery/ AJAX)
function(file) {
$.ajax({
type:"POST",
url:"getFile.php",
data:{fbpath: file},
success: function(data, textStatus, XMLHttpRequest) {
var popup = window.open();
popup.document.write(data);
}
});
});
Server Side (getFile.php)
<?php
$filename = $_POST['fbpath'];
$path = $filename;
header('Content-Description: File Transfer');
header('Content-Type: application/force-download');
header('Content-Disposition: attachment; filename='.basename($path));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($path));
ob_clean();
flush();
readfile($path);
exit;
?>
In the browser, the User will get a popup window containing the contents of the file.
This is still not a very pretty solution: The user won't get a nice download prompt, he is thus confronted with the binary data in a new browser tab. Not exactly what I hoped for, and on top of that, it's ugly!
Some more reading revealed that jQuery/ AJAX need some more handling to provide a user- friendly file download. It seems that John Culviner encountered this issue a while ago ("You can certainly use an XMLHttpRequest object to download a binary (or otherwise) file but there is nothing you can do to the response to somehow get it saved on the user’s computer.") and has a nice and handy solution approach for this.
In terms of getting a file out of a safe location and getting it to the client's browser I consider this question answered however.
I hope this helps anyone else in the future!
Mike
Allow users to download when authorised
- Store your files outside the document root.
- Have your own custom login mechanism
- Serve the file with php after you check whether the user is logged in, either with
readfile()
, or better if it's installed or you can install it, withmod_xsendfile
(which takes a load off PHP).
Related Topics
What Is Difference of Developing a Website in MVC and 3-Tier or N-Tier Architecture
How to Use PHP In_Array with Associative Array
Mp4 Plays When Accessed Directly, But Not When Read Through PHP, on iOS
How to Disable Mod_Security in .Htaccess File
How to Upload Multiple Image in Laravel
HTML Form PHP Post to Self to Validate or Submit to New Page
Aescrypt Decryption Between iOS and PHP
Yii2 Require All Controller and Action to Login
Inheritance of Static Members in PHP
How to Use PHPunit to Test a Function If That Function Is Supposed to Kill PHP
How to Check If a Value Already Exists to Avoid Duplicates
How to Eliminate PHP5 Strict Standards Errors
Programmatically Building Htpasswd