PDA

View Full Version : force to download a file



dead-poetic
11-17-2005, 09:41 PM
This is my code and it works, but it only downloads like 20 sec. of the MP3 file. What is wrong?
<?php

// force to download a file
$file = "http://localhost/test/".$_GET['file']."";

header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

header("Content-Type: application/force-download");
header( "Content-Disposition: attachment; filename=".basename($file));

header( "Content-Description: File Transfer");
@readfile($file);

?>
EDIT: FOUND ALTERNET CODE, PLEASE DON'T REPLY

BLiZZaRD
11-23-2005, 02:32 PM
AHHHHH!! Would you mind posting the code you found? or a link to it?? I am having the exact same problem, and I can't find another code that will download the full item!!!

dead-poetic
11-23-2005, 08:37 PM
http://elouai.com/force-download.php

mwinter
11-24-2005, 12:44 AM
http://elouai.com/force-download.phpDo not use that code under any circumstances. It introduces a security hole into your site by allowing a user to download any file that your server has permission to read. That includes returning PHP source code (and any secrets contained therein).

The best way to solve this problem is to educate your users. Tell them to "Right-click and select 'Save As...'.", or similar. It's simply not possible to 'force' a download in all cases, especially because IE doesn't implement HTTP properly (extraordinary behaviour for a browser!).

Mike

BLiZZaRD
11-24-2005, 09:01 AM
EEEK! That is scary! BUT...

couldn't you put something like:




$path = $HTTP_GET_VARS['filename'];
$path = str_replace('/', '', $path);
$path = str_replace('\\', '', $path);
$path = "c:/archive/".$path;



as this would eliminate the slashes, and thus only allowed downloads of files in the specified folder???? Then if all you had in that folder was an index and the file you wanted downloaded... would that work?

Twey
11-24-2005, 04:01 PM
As the page says...
If you expose this in a URL you are essentially posting a large sign titled "Hack me!"

What to do? Use literal values to represent your files that you would access, thus a value of "1" would represent the file xyz.pdf, a value of "2" would represent the file abc.mp3, and so on. Thus the only DOWNLOADABLE files are those specifically HARD-CODED in your script.You would want to add in checks here, either "hard-coded" values as mentioned above, or only files starting with, for example, "dlable_". Also, you need to remove all instances of "./" and "../" from the file path.

mwinter
11-24-2005, 05:53 PM
BUT...

couldn't you [...] eliminate the slashes, and thus only allowed downloads of files in the specified folder????There's more than one way in which that code could be secured, but it still doesn't address the fact that it isn't reliable.

If you have a directory full of files that are meant for downloading, it's simpler to use a .htaccess file:



<Files *.jpeg>
ForceType application/octet-stream
Header set Content-Disposition attachment
</Files>
This would prompt a download dialogue box for files ending in .jpeg in that directory (and deeper directories) in the majority of cases. Reminding users to use the 'Save as...' item in their context menu should take care of the rest.

You can use the FilesMatch (http://httpd.apache.org/docs/2.0/mod/core.html#filesmatch) directive to alter the headers for more than one file type, or remove the Files directive completely if all files in the same (or deeper) directory as the .htaccess file should be downloaded (that is, a dedicated downloads directory).

Mike

Dennis_Gull
08-20-2006, 01:40 PM
hmm how do you get this .htaccess to work?I dont have any .htaccess file on my server, do i just have to create a .htaccess and type in:
"<Files *.jpeg>
ForceType application/octet-stream
Header set Content-Disposition attachment
</Files>"
in the file and then upload it to my server? and what should the .htaccess file be named?
I want people to be able to force download one special type of files.

Sorry for all the probably stupid questions but one week ago I didnt even know how to create anything.

blm126
08-20-2006, 02:24 PM
Your server has to be Apache (http://httpd.apache.org) and has to allow you access to .htaccess. The file is simply named .htaccess

Dennis_Gull
08-20-2006, 06:16 PM
Okay is there any other safe way to make stuff get force download from just one folder or one type of files. :confused:
There should be some code you could write in a download.php file and still be safe.. I've seen a lot of big sites use it.

blm126
08-20-2006, 06:19 PM
Right Click, save as.

Twey
08-20-2006, 06:24 PM
I believe mwinter gave you an answer to that question in your thread.

mwinter
08-20-2006, 10:09 PM
Your server has to be Apache (http://httpd.apache.org) ...

That's not entirely true: other servers implement .htaccess files, and others will offer similar systems albeit with different filenames and directives.




There should be some code you could write in a download.php file and still be safe.

It's no more special than a server directive and no more reliable.

Mike

blm126
08-21-2006, 01:43 AM
[about needing apache]That's not entirely true: other servers implement .htaccess files.

I'm not doubting you, but what servers?

Dennis_Gull
08-21-2006, 10:18 PM
I found a code thats allow you download just one type of files



<?php
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'off');

// Prevent abuse
$file = str_replace(array("/", ".."), "", $file);

//set allowable extensions
$ext_filter = array("what type of file you wanna allow");

//get file extension
$file_extension = strtolower(substr(strrchr($file,"."),1));

if (!in_array($file_extension, $ext_filter)) {
exit('File Type Invalid');
} else if (!file_exists($file)) {
exit('File Does Not Exist');
}

//do download
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
header("Content-type: application/force-download");
header("Content-Disposition: attachment; filename=\"".basename($file)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($file));
readfile("$file");
exit();
?>

although it had:


$file = '';
if (isset($_GET['d'])) {
$file = $_GET['d'];
} else {
exit('No file passed'); //no file passed so kill the script
}

implanted at the start but it blocked any types of files to get downloaded and i dont know why.

mwinter
08-22-2006, 04:58 PM
I'm not doubting you, but what servers [other than Apache use .htaccess]?

Zeus is the most popular server apart from Apache that uses .htaccess files. The directives aren't identical in all respects, but most are. Also, any Apache derivative would use .htaccess files. I think there are one or two other separate servers that do, but I don't recall their names.




I found a code thats allow you download just one type of files

It's still no better than using the server alone. For example, without a fair amount of effort, ranges won't be supported; a necessity to permit resumeable downloads.

To answer your earlier question: yes, all you should need to do is create a text file named .htaccess containing a Files directive. The example I posted earlier is for files with the .jpeg extension. For PNG files, you'd change the first line to:



<Files *.png>

If you wanted to handle multiple types, like PDF and Microsoft Word, you'd use a FilesMatch directive:



<FilesMatch "\.(pdf|doc)$">

and change the last line to:



</FilesMatch>

Mike

Steganos
01-21-2007, 11:30 PM
Sorry to bring this oldie back but I gota little "solution/problem" type of thing here. I did a "check" to allow only one type of file, and see if it would work. It works, but I don't know if there are any exploits to it.


<?php

$filename = $_GET['file'];
$test=explode(".", $filename);
if($test[1]!="avi"){
echo("Trying to hack? heh.");
exit;
}
elseif($test[1]=="avi){
// required for IE, otherwise Content-disposition is ignored
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');

// addition by Jorg Weske
$file_extension = strtolower(substr(strrchr($filename,"."),1));

if( $filename == "" )
{
echo("You need to specify a file.");
exit;
} elseif ( ! file_exists( $filename ) )
{
echo("Specified file does not exist.");
exit;
};
switch( $file_extension )
{
case "exe": $ctype="application/octet-stream"; break;
case "zip": $ctype="application/zip"; break;
case "avi": $ctype="video/avi";break;
default: $ctype="application/force-download";
}
header("Pragma: public"); // required
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false); // required for certain browsers
header("Content-Type: $ctype");
// change, added quotes to allow spaces in filenames, by Rajkumar Singh
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
exit();
}
?>

microcode
01-28-2007, 05:38 PM
I saw your code and I would like to use it in my site, but I am EXTREMELY newbish, and don't know which parts of the code to change to make it point to my file. I'm slowly learning the code and all, but I need to make a link which downloads these stupid files I need to share, and I want to make it to where it's not a right click situation, because I'm using flash buttons, and they don't permit a right click. ANYhow, I hope anyone with the knowledge can help me, as I've been searching far longer than I had hoped for a solution.

jacksont123
01-29-2007, 03:22 PM
Will these scripts work, if you are making a force download from another server?
This means the file you are forcing to download is not on your own server but another.
ie: http://www.dynamicdrive.com/forums/designfiles/logo.gif

tech_support
01-30-2007, 02:37 AM
Wouldn't that be illegal?

Twey
01-30-2007, 02:21 PM
Hm. Interesting question. The file's not actually being copied to the server, so I don't think so (see web proxies like PHProxy for examples of this happening elsewhere).

However, I'm not a lawyer.

BLiZZaRD
01-30-2007, 02:28 PM
Not being copied no, but you are using that sites bandwidth, which they are paying for, so there is some theft involved. Just depends on how deep you want to look.

The best bet is to contact that site and ask permission, or for a copy of said file to host for download on your site.

Twey
01-30-2007, 02:35 PM
That's known as hotlinking, which is considered impolite, but not, insofar as I'm aware, illegal.

BLiZZaRD
01-30-2007, 02:41 PM
Depends on how good your lawyer is :D

But again, there are ways to prevent hotlinking as well... I know my web host offers a script to prevent it in cpanel. I am sure there are php, javascript and .htaccess codes as well...

Twey
01-30-2007, 02:46 PM
Aye, there are (PHP and .htaccess, at least). However, these generally rely on the contents of the Referer header, which can be modified.

BLiZZaRD
01-30-2007, 04:19 PM
Then it becomes a coding war. And for what? someone else's zip file?

Just ask if you can host it for a download and save the trouble :D

djr33
01-31-2007, 01:36 AM
Nah. There's no difference in using someone's bandwidth from a server or an end computer. If you view an image on a website, you're stealing their bandwidth.
Sure, might be impolite, but I don't see it as stealing... I don't think you could distinguish that.

AnalogPanda
01-31-2007, 08:16 PM
I know mwinter is dead set against using eLouai's script, but I have been using it without issue on a windows (server 2000 I think) IIS5 box with PHP 4.3.10 for the purpose of downloading only zip files (I added a mysql query that tracks downloads)

We've upgraded to Windows Server 2003 and PHP 5.2 and now the script appears to still work, but the zip files are corrupted when downloaded through the script (they are not corrupted otherwise)

I'm curious to see if anyone else has run into this, and what a possible work around might be.

Since the Elouai script is a security concern, and zip files don't require a forced-download, perhaps there is another method to track my downloads?

Twey
01-31-2007, 08:47 PM
<?php
# download.php

# ... tracker code ...
header('Location: ' . $_GET['url']);
?>
<a href="download.php?url=http&#37;3e%2f%2fwww.site.com%2fpath%2fto%2ffile.zip">Download</a>

AnalogPanda
01-31-2007, 10:50 PM
Twey,

That's perfect. I really should have thought of that!
Thank you

This (and my previous method) will still "count" a download even when someone clicks the link but then closes/cancels the browser prompt for the download.

Might there be a way to check the server to see if the file really was downloaded/requested? Perhaps I'd have to involve an IP address check...

djr33
01-31-2007, 11:06 PM
I think that might be getting a little deeper than PHP, but I'm not sure. I think you would need to access the server log files. At that point, no need to worry about tracking as the hits occur. Just look at the log files anyway. Then count requests. Unless you need to know specifically who did what. Then do compare IP to request, but.. again, no need to track, because if you have the IP on the request anyway... you've... got the IP anyway ;)

Twey
02-01-2007, 12:05 AM
I think that might be getting a little deeper than PHP, but I'm not sure.Aye, you have it.

CurrentWave
04-17-2007, 07:33 PM
Sorry to bring this tread back to life.... but this saga continues.

I have been using the htaccess solution for forcing downloads for years, but now newer versions of IE are bypassing this trick - yeeeek:eek:

The code posted by Dennis_Gull looked promising - does anyone have a link to it?

Using php to force download can be a big security leek, as pointed out in this post - and I've read before about assigning numbers to file names to help with this issue, however I cannot seem to find a code example of this being done! Seems like all the code examples out there are the ones ignoring security! And I'm not fluent enough to write my own.

Does anyone have an example of php to force download that uses specific naming assignments in it?

Thanks for any and all updates to this topic :)

pcbrainbuster
04-17-2007, 07:43 PM
Well its possible using ActiveX with JavaScript for IE and I know how to do it but just no point, it will take alot of time to load the page and there is a prompt which comes up but there is no problem to that as people usually press yes...

boxxertrumps
04-17-2007, 11:19 PM
Then there's more than a third of users that use things that aren't micro$haft, and wont be able to receive the download at all.

Twey
04-18-2007, 09:32 AM
A third? Web statistics are notoriously unreliable, but that's still rather optimistic. I'd say a quarter at most. However, of that 75&#37;, some have ActiveX disabled.

tech_support
04-18-2007, 10:39 AM
But, if ActiveX was disabled, then Windows Update can't start.

Twey
04-18-2007, 04:25 PM
Not necessarily... it can be enabled/disabled on a per-site basis using IE's security zones, and the native Windows Update interface can be used rather than the web-based one.

cancer10
02-22-2009, 12:49 PM
Herez one I found on the internet


function output_file($file, $name, $mime_type='')
{
/*
This function takes a path to a file to output ($file),
the filename that the browser will see ($name) and
the MIME type of the file ($mime_type, optional).

If you want to do something on download abort/finish,
register_shutdown_function('function_name');
*/
if(!is_readable($file)) die('File not found or inaccessible!');

$size = filesize($file);
$name = rawurldecode($name);

/* Figure out the MIME type (if not specified) */
$known_mime_types=array(
"pdf" => "application/pdf",
"txt" => "text/plain",
"html" => "text/html",
"htm" => "text/html",
"exe" => "application/octet-stream",
"zip" => "application/zip",
"doc" => "application/msword",
"xls" => "application/vnd.ms-excel",
"ppt" => "application/vnd.ms-powerpoint",
"gif" => "image/gif",
"png" => "image/png",
"jpeg"=> "image/jpg",
"jpg" => "image/jpg",
"php" => "text/plain"
);

if($mime_type==''){
$file_extension = strtolower(substr(strrchr($file,"."),1));
if(array_key_exists($file_extension, $known_mime_types)){
$mime_type=$known_mime_types[$file_extension];
} else {
$mime_type="application/force-download";
};
};

@ob_end_clean(); //turn off output buffering to decrease cpu usage

// required for IE, otherwise Content-Disposition may be ignored
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');

header('Content-Type: ' . $mime_type);
header('Content-Disposition: attachment; filename="'.$name.'"');
header("Content-Transfer-Encoding: binary");
header('Accept-Ranges: bytes');

/* The three lines below basically make the
download non-cacheable */
header("Cache-control: private");
header('Pragma: private');
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");

// multipart-download and download resuming support
if(isset($_SERVER['HTTP_RANGE']))
{
list($a, $range) = explode("=",$_SERVER['HTTP_RANGE'],2);
list($range) = explode(",",$range,2);
list($range, $range_end) = explode("-", $range);
$range=intval($range);
if(!$range_end) {
$range_end=$size-1;
} else {
$range_end=intval($range_end);
}

$new_length = $range_end-$range+1;
header("HTTP/1.1 206 Partial Content");
header("Content-Length: $new_length");
header("Content-Range: bytes $range-$range_end/$size");
} else {
$new_length=$size;
header("Content-Length: ".$size);
}

/* output the file itself */
$chunksize = 1*(1024*1024); //you may want to change this
$bytes_send = 0;
if ($file = fopen($file, 'r'))
{
if(isset($_SERVER['HTTP_RANGE']))
fseek($file, $range);

while(!feof($file) &&
(!connection_aborted()) &&
($bytes_send<$new_length)
)
{
$buffer = fread($file, $chunksize);
print($buffer); //echo($buffer); // is also possible
flush();
$bytes_send += strlen($buffer);
}
fclose($file);
} else die('Error - can not open file.');

die();
}

/*********************************************
Example of use
**********************************************/

/*
Make sure script execution doesn't time out.
Set maximum execution time in seconds (0 means no limit).
*/
set_time_limit(0);
$file_path='that_one_file.txt';
output_file($file_path, 'some file.txt', 'text/plain');