File Handler returning garbled files
For the past 3 months my site has been using a PHP file handler in combination with htaccess. Users accessing the uploads folder of the site would be redirected to the handler as such:
RewriteRule ^(.+)\.*$ downloader.php?f=%{REQUEST_FILENAME} [L]
The purpose of the file handler is pseudo coded below, followed by actual code.
//Check if file exists and user is downloading from uploads directory; True.
//Check against a file type white list and set the mime type(); $ctype = mime type;
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");
header("Content-Disposition: attachment; filename=\"".basename($filename)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($filename));
readfile("$filename");
As of yesterday, the handler started returning garbled files, unreadable images, and had to be bypassed. I'm wondering what settings could have gone awry to cause this.
-EDIT-
Problem found, but not resolved. Including a path to a php library I was using for integrating with Wordpress was corrupting the files. Removing that block of code solves the 开发者_运维知识库corruption issue but leaves the files accessible without the desired authentication.
@include_once($_SERVER['DOCUMENT_ROOT'].'/wp-blog-header.php');
if(!is_user_logged_in())
{
auth_redirect(); //Kicks the user to a login page.
}
//resume download script
Maybe more tests will reveal the problem...
if ( !isset($filename) ) {
die('parameter "filename" not set');
}
else if ( !file_exists($filename) ) {
die('file does not exist');
}
else if ( !is_readable($filename) ) {
die('file not readable');
}
else if ( false===($size=filesize($filename)) ) {
die('stat failed');
}
else if ( headers_sent() || ob_get_length()>0) {
die('something already sent output.');
}
else {
$basename = basename($filename);
header("Pragma: public");
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");
header("Content-Disposition: attachment; filename=\"".$basename."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".$size);
readfile($filename);
}
How are the files corrupted? Truncated? 0-byte? Completely different content? Random sections replaced with garbage?
Is it possible the server's PHP memory limit has been lowered? readfile() will buffer the whole file in memory before outputing it. Therefore a 40meg file will fail is the memory limit is 39.9999, kind of thing.
For streaming a file to the user, it's best to NOT use php's own "dump file to browser" functions, as they're all subject to the memory limit. It's best to do an fopen/fwrite/fclose loop and spit the file out in small manageable chunks (4k, 16k, etc...).
精彩评论