I'm developing a web service. With this service, user's will upload their .php files, and service will remove UTF8 BOM characters from php file. And then, There will be a link like this :
Download Your File
But when i click this link, browser browsing to this file. I don't want browse it, i want to download it. So , when user click this link, downloading will start.
Any ideas ?
(P.S. I don't want modify uploadedfile.php file, also i read 5 questions about this, but still i have problem.)
You need to supply this HTTP header:
Content-Disposition: attachment; filename=example.txt
You can usually specify this for entire directories at a time by configuring your web server appropriately. If you mention which web server you are using, somebody may be able to suggest how to do this.
The problem is that you're allowing people to upload PHP files on your server, then giving them a link to execute that PHP file. The web server is automatically treating those uploaded PHP files like any other PHP file, i.e. executing it, which opens you up to a massive security hole.
Whatever purpose your web service has, I'd suggest renaming the file on your server when it is uploaded (something 'random' is best, without an extension), then having a PHP script feed it back out with the appropriate headers set when it is requested.
The URL for such a script would look like:
http://www.example.com/get_uploaded_file.php?id=jgh3h8gjdj2389
It would link the value in id with the file on the server, and if you've saved the original filename somewhere (flat file, DB), you can serve it out using its original name, so long as you set the right HTTP headers.
Linking directly to the PHP file may end up executing it. One way is (like somebody above suggested) to rename it. Or, you can have a downloader.php which does below:
<?php
header('Cache-Control: no-cache, must-revalidate');
header('Expires: Mon, 01 Jan 2000 01:00:00 GMT'); // some date in past
header('Content-type: text/plain');
header('Content-Disposition: attachment; filename='.basename($filepath));
header('Content-Length: ' . filesize($filepath));
flush(); // or any other flush function/mechanism you use.
readfile($filepath);
and link it something like:
Download
This method will let you retain the .php extension. Also, if the PHP file is big and connection is slow, they progress-bar would be accurate (because you've flushed the content length upfront.
Related
I'm currently attempting to find the source path to a file which I am downloading from it. I'm not sure how to explain, but I'm going to try my best.
I send a GET request to our server, it looks like: GET /en/download.php?pod_id=2138. From here the server proceeds to reply:
Set-Cookie: PHPSESSID=neatgv4m7a1pdorjqmoo76s151; path=/
Content-disposition: attachment; filename=2015-09-06-2.wav
Connection: close
Transfer-Encoding: chunked
Content-Type: Application/octet-stream
I captured this of course using WireShark. Next comes the full WAV file, but I'll spare us that.
I'm curious whether I can get the path to 2015-09-06-2.wav so that I might be able to download the file that way. Is this possible or will the server not permit me to do this?
Download.php will provide the file for security reasons. So if secured enough its not possible to directly access the file or folder directly. The direct access might be blocked using .htaccess or it might be placed where the public can't access directly or the file(blob) might be fetched directly on request from the database on request by the download.php
If its not protected from direct access the file is somewhere in the server in some folder. You could access if you find it. but there is no way to find it without a clue. browse through you might find.
Most likely direct access would be unavailable if its provided by the php file in that way.
In short: not possible:
The Downloads folder is a browser-specific location that only the user has control of. The file will download to the folder that is specified by the user.
Use readfile along with header to force a Save As... dialog to appear.
<?php
header('Content-disposition: attachment; filename=image.jpg');
header('Content-type: image/jpeg');
readfile('/server/path/to/image.jpg');
?>
Get the path of the downloads folder in PHP?
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 :)
I've been developing a new web application which relies on Amazon S3 servers as storage system, and Codeiginter as the PHP framework.
I need to force the file to download when the link is clicked. The original URL looks like this:
http://www.our-web.com/download/do/1.jpg
which generates a temporary signed URL to the actual file on the Amazon S3 servers like this:
http://main_bucket.s3.amazonaws.com/post/1/1.jpg?AWSAccessKeyId=AKIAJEOQKYPKC3CCU5RA&Expires=1305395426&Signature=iuzCdA22gImLK192%2BMAhk8OkAY8%3D
I need to make the file start downloading from the real Amazon URL it soon as the user clicks the link.
I have two ways now to do so:
Use redirect() which will open the file not download it; or
Alter headers as this code:
header('Content-type: application/force-download');
header('Content-Disposition: attachment; filename=' . $file_name);
header('Content-Transfer-Encoding: binary');
header('Expires: 4000');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($generated_file));
readfile($generated_file);
Unfortunately, both ways don't help me. The second method causes the download to come from my website and not from directly from Amazon.
How can I force the file to download directly from the Amazon S3 servers, and not from my website?
You just need to set the correct headers on your files in S3 in order to force the browser to download rather than opening the file. Set these:
Content-Disposition: attachment; filename=FILENAME.EXT
Content-Type: application/octet-stream
You will need to set them when uploading the files to S3. With the php SDK you'd use create_object.
Or you can set these after uploading using 'change_content_type' or by copying the file to itself in S3 and setting the correct headers.
Bit late to the party, but often times with a file you don't want to have to decide at storage time how it will be used. You want to be able to store the file once, then in one area possibly embed the file or display in browser, and in another area enable the user to download the same file.
Fortunately you can do that by providing override parameters in the request url. It only works with signed requests, but thankfully you're already doing that.
If you add a parameter like &request-content-type="application/force-download" that should do the trick.
Check out the Request Parameters section of the S3 GET Object documentation:
http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
The question is about setting this programmatically, but to do so manually through the AWS console:
Select a file in S3.
Properties > Metadata > Add more metadata
Key: Content-Disposition Value: Attachment
Save
If your S3 website is behind CloudFront, don't forget to invalidate the file before trying again! That will clear the cached file.
You can get to the invalidation page by going to:
Cloudfront -> Distribution Settings -> Invalidation
Then enter the path to the file.
It will take some time to invalidate the file. For my file it took 5 minutes.
You should use $s3->getObject()'s ResponseContentDisposition parameter to set the Content Disposition of the response. You can use this to force download a file.
$s3->getObject([
"Bucket" => "example",
"Key" => "example.txt",
"ResponseContentDisposition" => 'attachment; filename="example.txt"'
]);
You can't tell the browser how to handle a remote file. By redirecting to amazon you're telling the browser to start a new request over there. You don't have any control over that request.
The only solution I can think of is to package the image into a zip file or similar. Of course that adds another (probably annoying) form of complexity.
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
Firstly: I'm a lowly web designer who knows just enough PHP to be dangerous and just enough about server administration to be, well, nothing. I probably won't understand you unless you're very clear!
The setup: I've set up a website where the client uploads files to a specific directory, and those files are made available, through php, for download by users. The files are generally executable files over 50MB. The client does not want them zipped, as they feel their users aren't savvy enough to unzip them. I'm using the php below to force a download dialogue box and hide the directory where the files are located.
It's Linux server, if that makes a difference.
The problem: There is a certain file that becomes corrupt after the user tries to download it. It is an executable file, but when it's clicked on, a blank DOS window opens up. The original file, prior to download opens perfectly. There are several other similar files that go through the same exact download procedure, and all of those work just fine.
Things I've tried: I've tried uploading the file zipped, then unzipping it on the server to make sure it wasn't becoming corrupt during upload, and no luck.
I've also compared the binary code of the original file to the downloaded file that doesn't work, and they're exactly the same (so the php isn't accidentally inserting anything extra into the file).
Could it be an issue with the headers in my downloadFile function? I really am not sure how to troubleshoot this one…
This is the download php, if it's relevant ($filenamereplace is defined elsewhere):
downloadFile("../DIRECTORY/files/$filenamereplace","$filenamereplace");
function downloadFile($file,$filename){
if(file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$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: ' . filesize($file));
# flush();
readfile($file);
exit;
}
}
ETA Additonal Info:
- Tests for working/non-working files have been done on the same machine
- If it makes any difference, the original file has a custom icon. After download, the file has a generic blank document icon.
Additonal Info: I THINK THIS ONE'S IMPORTANT!
I just tried downloading the file directly (to bypass the download link that triggers the download function above). If I download the file by just going to its url and downloading it that way, the downloaded file WORKS. So I'm thinking it must have something to do with the download function. But what??
3/17 MAJOR CORRECTION —AND RESOLVED—
So I woke up this morning and it dawned on me that maybe I was comparing the files wrong. (I had re-saved them as binary text, and then compared them. I didn't realize the comparison program would take and compare actual exe files). This morning I tried comparing the actual exe files and there is a difference. There was one line of php code that was being injected into the first line of the file. I adjusted the php, and the problem was fixed. (It was from the if/else statement that defined teh $filenamereplace variable in the code I'd cited). Thanks again for all your help, and sorry for misleading you in insisting that the files' contents were identical!
"I've also compared the binary code of the original file to the downloaded file that doesn't work, and their exactly the same (so the php isn't accidentally inserting anything extra into the file)."
If that's really true, then the problem must be in how the exe is started after it has been downloaded. It should certainly not be a problem with your PHP code.
Perhaps they were corrupted on upload. This can happen if you transfer them via FTP in ASCII mode instead of BINARY.