PHP File Transfer - php

Suppose I have a device that knows the name of a file they want to download from my server.
How can I transfer said file to that device without giving the device access to the file system?
For example, suppose I have a page ping.php which receives a get request for "something.zip"
ping.php knows the location of something.zip (somewhere on the server's file system), but I can't allow the user access to the file system, or allow them to know the location of the file (it even needs to be hidden from somebody using something like wireshark).
How can I solve this problem?
It might be an easy solution, I'm just not extremely well versed in these matters.
If it makes any difference I'll be using an Apache server on a Linux box.

You can create a PHP script to facilitate the file transfer while the file is sitting in a folder that is not accessible via the Web. This is how I commonly handle file downloads on my system.
There are any number of sample scripts that you may use to do the actual file transfer. The key is to put that file outside the web-accessible file system.
For completeness, here's some code I've used in the past to do a file download in PHP:
$filename="filetodownload.xyz";
$cf = realpath("/non-webaccessible-folder/".$filename);
$file=$cf;
header('Content-Disposition: attachment; filename="' . basename($cf) . '"');
header("Content-Length: " . filesize($cf));
header("Content-Type: application/octet-stream");
readfile(realpath($cf));

Related

PHP: make uploaded file in root folder (not in public_html) accessible only for logged in user?

I made an upload form and stored files in (root) '../upload_file' folder
the problem is how authorized (logged in) user only can download that uploaded files? because browser cant handle root url like: www.web.com/../upload_file/test.pdf
please Im newbie
thanks :D
Ok, I want make this clear. Actualy my question is similar to: How to go about protecting files from unauthorized downloads
where is unsolved
You need to create a php script that will authenticate the user and then output the contents of the uploaded file in the php script. Make sure you do not echo anything out before setting the headers of the output file. There should be code somewhere on how to load the contents of a file into a variable.
$fileContents = file_get_contents("test.pdf");
header("Cache-Control: public, must-revalidate");
header("content-disposition: attachment; filename=test.pdf");
header("content-type: application/pdf");
header("content-length: " . strlen($fileContents));
//output file contents
echo $fileContents;
The users should never have access directly to the upload folder.
Access control depends on your server operating system and server, like Windows Server(IIS) or Linux(Apache, Ngix)...
The best way to protect the files from being visted from URL is to set the upload path outside your webroot.And the file will be only accessiable by your server.
UPDATE: how to access files by user
Create a php file named readpdf.php.
header("Content-type:application/pdf");
//if you just want to show the file on broswer, delete the line below
header("Content-Disposition:attachment;filename='downloaded.pdf'");
readfile("progit.pdf");
Read the file by visiting http://localhost/readpdf.php

Apache logging specific data

I'm trying to access Apache's access log and track bandwidth usage in real time.
As we know Apache logs every request made to the server, and the log file could be as big as couple of gigabytes so it would be pretty hard for PHP to phrase the log file.
Lets say I provide users with download links with special URL prefix.
Is there a way to only log those download links? if so I could use multi-log to log those data to another log file. Multi Log
Apache config uses %u to represent the URL, is there a way to filter this?
Have php create the logfile, When people click on the link php could add to the log file and output what file to download.
<?php
/*Logging Proccess*/
$file = "Log.txt";
$to_be_logged = file_get_contents($file);
$to_be_logged .= /*Insert what you want to log like ip address or sumthing*/$_SERVER['REMOTE_ADDR']."\n";
file_put_contents($file, $to_be_logged);
/*Download Script*/
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename=filetobedownloaded.rar");
?>
Put this in a file named download.php or something and
Add a file named Log.txt next to the file where this is located
This should work...

Allow users to view or download files, but not execute them

I'm making a website that allows users to upload any kind of files. All the files are stored in a 'files/' folder. I want users to be able to see and download any of the files in that folder, but not execute them.
Some files, like JPG files, are easy: browsers have a built in system to display them, as we all know.
Other files, like PDF files, cannot be displayed directly, so what browsers do is prompt the user a download screen. Thats fine too.
However, other files, like PHP files, pose a problem. When I open one such file, if I am not careful, it gets executed. For example, if the file contains
<?php echo 'ok'; ?>
then when I go to 'files/myphpfile.php', what I see is just 'ok', and not the contents of the file. This will happen not only with PHP files, I presume, but also with any other file types that my server can execute (for example ASP files, I suppose).
What I need is to prevent users from executing the files, so that when they go to 'files/myphpfile.php', they either see the file contents, or the browser prompts them the download screen. Ideally it would be a general solution, that would not require me to construct a list of every file type executable by the server.
To me, the most reasonable approach was to set the file permissions to 644, so that users could only read the files. But after doing so, the PHP files keep getting executed.
To download:
header('Content-Type: text/plain');
header('Content-Disposition: attachment; filename=somefile.php');
header('Pragma: no-cache');
readfile('/path/to/somefile.php');
To display:
echo htmlspecialchars(file_get_contents('/path/to/somefile.php'));
UPDATE
When I open one such file, if I am not careful, it gets executed
That's why you don't have a direct link to the file. I.e. the files should not get uploaded to a location somewhere inside the document root. If the files are uploaded to a directory outside the document root people cannot access it directly by URL, e.g.: http://example.com/files/somefile.php.
This will happen not only with PHP files, I presume, but also with any other file types that my server can execute (for example ASP files, I suppose).
You are the only one who can tell what files may be dangerous. We cannot know whether you have some server which also serves (and parses) asp files nor what other potential dangerous files may be on there.
What I need is to prevent users from executing the files, so that when they go to 'files/myphpfile.php', they either see the file contents, or the browser prompts them the download screen.
See my already provided solutions above.
To me, the most reasonable approach was to set the file permissions to 644, so that users could only read the files. But after doing so, the PHP files keep getting executed.
This is because PHP files are not getting executed. They are only being parsed (i.e. being read by the webserver).
So basically you need to find out what are the possible dangerous file types (also think about other files that may get parsed by PHP e.g. .phtml or what not). Users shouldn´t be able to call executables anyway through a HTTP request. Because when I would do http://example.com/run-virus.exe it never gets executed on the server, but it only requests the file.
I solved the problem by displaying the files through a script, and not directly from their real location. The script was taken from an answer to a previous question of mine.
$fileinfo = finfo_open( FILEINFO_MIME_TYPE );
header( 'Content-Type: ' . finfo_file( $fileinfo, 'files/' . $filename ) );
finfo_close( $fileinfo );
readfile( 'files/' . $filename );
So now users that want to see 'files/myfile.php' must go to 'open/myfile.php', and the script above will prompt them the download screen, or display the file in the browser, if the browser is able to do so.

Create dynamic, expireable and resumable download link using php, apache

Here is the problem details:
1) I want to create dynamic (ip based) download link. So user can't download the file with different IP with the same download link.
2) Before start the actual download, i want to log this download request using php and perform some checks (verify the http referrer) to allow the user to download the actual file.
3) I also want the download file to be resumable and could be downloaded with download manager (with multiple download instances). Also want to limit the maximum number of allowed instances for each download.
4) The file size could be more than 200 MBs.
So, the solution which i am thinking is to create the download link with the md5 hash of user's ip. E.g.
http://yourdomain.com/download.php?ip_hash=hash-of-the-ip&file=file-to-download
This is just a example but we can also create a nice link of this using htaccess.
What should i do next? I tried to do it using
header("Content-Type: $ctype");
header("Content-Length: " . filesize($file));
header("Content-Disposition: attachment; filename=\"$fileName\"");
readfile($file);
But using this the download does not remain resumable for the end user.
Is this fine to send large files using this method?
After doing some research, I came to know that the .exe files become corrupted for the end user using this way.
After doing some more research, i have found the answer of my question. I just thought i should share it with you guys as well.
As rambo commented, we can use mod_xsendfile module of the apache server. We need to enable it if its disabled.
Here is the link to download the module files if your apache does not have this module. Its available for mostly all the versions of apache and available for both x32 and x64.
https://github.com/nmaier/mod_xsendfile
You can use the following code to send the file using this apache module after doing all your custom validations.
<?php
//We want to force a download box with the filename hello.txt
header('Content-Disposition: attachment;filename=hello.txt');
//File is located at data/hello.txt
header('X-Sendfile: data/hello.txt');
?>
I hope it will help you guys :)

How to let users with required permission download a file via php?

I have a php file that acts as a gatekeeper for all the files I want people to download, who ahve sufficient privilages.
The code I use throw the file to the user is
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-disposition: attachment; filename=\"".$public_filename."\"");
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: ".$f_filesize);
readfile($file_path);
Most files are fairly large.... 400mb-10GB.
What would be a good way to do this, and keep the true locations + filenames secret, so people cant just link to the files directly, but HAVE to link thru my download.php?file=ID gatekeeper?
Thanks
EDIT: Im not asking how to do user authentication, all that is done. Im just asking if my way of doing it, is a good idea on a large scale. Seems like it could cause memory problems if I keep reading 10GB files.
Ok, having php send files of around 400Mb–10Gb is not good. You need to somehow let whatever webserver you're using actually serve the files.
This really comes down to how secure you need it to be. The easiest solution that comes to mind (but far from the most secure) is using symbolic links with long random names that link to the original file. After a certain time the symbolic links expire and are removed. Each user get their own symbolic link (or "token") to the file they're downloading. I'm not sure how this plays out in Windows-environment, but on unix it's fairly straightforward anyway.
Here's some pseudo code:
if($user->isAllowedToDownload($file)){
$token = md5($user->name . $file->name . time() . $someGoodRandomValue);
symlink($file, $download_path . $token);
header("Location: $download_url$token");
}
Then you need a cron job that cleans out old symbolic links. You also need to make sure the webserver is set to follow symbolic links, preferably only for that folder where these download tokens are created.
So when the user maybe requests domain.com/download?file=bigfile.mp4 a symbolic link is created in the webservers public space that points to the real file outside the webservers public space. The user gets redirected to maybe domain.com/getFile/ab739babec890103bdbca72 which in turn causes the webserver to serve the file. Now it's very hard for users to try and guess what an URL is for a file, and that's the "security".
You're already doing that - the $public_filename is what you want it called, the readfile($file_path) part is the file - it's location isn't made public. Past that, it could be above the document root.
Put the files somewhere that is not accessible via HTTP.
Create a database table of file IDs with file paths.
Link to the files via file ID (as you noted above, download.php?fileID=0000).
???
Profit.
As someone who did this previously (many years ago), you need to consider the memory impact this will have on your server. The readfile function was not available then, so it is possible you may not need to do anything special for memory considerations.
You'll want to somehow authenticate them (an HTML form, HTTP basic auth, whatever), then set a session flag, which your download.php script can check. Note that this doesn't prevent people from downloading the file, then distributing it themselves.
You should configure your web server so the real files are not directly accessible.
It's not going to cause memory problems per se. readfile does not read the file into memory. However, using PHP will create overhead. You can eliminate some of this delay by using X-Sendfile.
Your method will cause memory problems, however it is possible to read and output the file in chunks. You will need to use flush() function after you echo each chunk of file. You can also make resuming downloads to work with a little more effort. Still this is an CPU hungry approach.
The easier and better solution is to use "x-sendfile" header tag supported by both apache and lighttpd through their modules. All you'll have to do is just specify file name in your header, similar to this:
header('X-Sendfile: filename-on-your-file-system');
Link for lighttpd:
http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file

Categories