我编写了一个PHP页面,用于从服务器下载文件。该文件的名称作为GET变量在URL中传递,然后以下代码提供该文件以供下载:
$filepath = "/path/to/files";
$filename = $_GET['id'];
if( ! file_exists($filepath . "/" . $filename) )
{
header("HTTP/1.1 404 Not Found");
@session_destroy();
exit(0);
}
$cmd = '/usr/bin/stat -c "%s" ' . $filepath . "/" . $filename;
$out = array();
$ret = 0;
exec( $cmd, $out, $ret );
header('Content-type: application/octet-stream');
header('Content-Length: ' . $out[0]);
header('Content-Disposition: attachment; filename="' . $filename . '"');
readfile($filepath . "/" . $filename);
注意:我正在使用exec()调用,因为大多数文件都很大(> 2GB),较大的文件导致filesize()和stat()函数失败。
无论如何,这段代码几乎适用于所有文件。但是,当文件正好大小为2 GB(2147483648字节)时,不会发送任何标头,并且浏览器会尝试下载PHP页面本身,这会导致保存一个名为download.php的空文件。
这是我用curl测试时发生的事情:
测试#1:获取名为bigfile1的1 GB文件:
$ curl -v http://<SERVER>/download.php?id=bigfile1
* About to connect() to <SERVER> port 80 (#0)
* Trying <IP_ADDRESS>... connected
* Connected to <SERVER> (<IP_ADDRESS>) port 80 (#0)
> GET /download.php?id=bigfile1 HTTP/1.1
> User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.8 libssh2/0.18
> Host: <SERVER>
> Accept: */*
>
< HTTP/1.1 200 OK
< Date: Tue, 21 Jun 2011 19:10:06 GMT
< Server: Apache/2.2.4 (Unix) mod_ssl/2.2.4 OpenSSL/0.9.8c PHP/5.3.0
< X-Powered-By: PHP/5.3.0
< Set-Cookie: PHPSESSID=382769731f5e3782e3c1e3e14fc8ae71; path=/
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
< Pragma: no-cache
< Content-Length: 1073741824
< Content-Disposition: attachment; filename="bigfile1"
< Content-Type: application/octet-stream
<
* Connection #0 to host <SERVER> left intact
* Closing connection #0
测试#2:获取一个名为bigfile2的2 GB文件
$ curl -v http://<SERVER>/download.php?id=bigfile2
* About to connect() to <SERVER> port 80 (#0)
* Trying <IP_ADDRESS>... connected
* Connected to <SERVER> (<IP_ADDRESS>) port 80 (#0)
> GET /download.php?id=bigfile2 HTTP/1.1
> User-Agent: curl/7.18.2 (i486-pc-linux-gnu) libcurl/7.18.2 OpenSSL/0.9.8g zlib/1.2.3.3 libidn/1.8 libssh2/0.18
> Host: <SERVER>
> Accept: */*
>
* Empty reply from server
* Connection #0 to host <SERVER> left intact
curl: (52) Empty reply from server
* Closing connection #0
我创建了1 GB,2 GB,3 GB,4 GB和5 GB大小的测试文件。 2 GB文件是导致此行为的唯一文件,但它始终如一地发生,并且无论客户端浏览器或操作系统如何,它似乎都会发生。服务器正在运行Debian GNU / Linux 4.0,Apache 2.2.4和PHP 5.3.0。
任何想法都将不胜感激。谢谢!
答案 0 :(得分:3)
在64位安装上,我对使用PHP 5.3.6提到的文件大小没有任何问题。一个32位的安装给了我:
failed to open stream: Value too large for defined data type
但是说,我甚至不会使用readfile
。代替:
header("X-Sendfile: $filepath/$filename");
需要安装和配置mod_xsendfile。
答案 1 :(得分:1)
我把它放在评论中,但格式化没有帮助,所以我也会将其作为答案发布,尽管它没有回答你的问题。如果攻击者访问此网址会发生什么?
http://yourserver.com/download.php?id=../../../../etc/passwd
注意以查看id
GET变量中的目录遍历。
$realfile = realpath($filepath .'/'. $filename);
if (substr($realfile, strlen($filepath)) == $filepath && file_exists($realfile))
{
do_the_download();
} else {
die('file not found (or you are a bad person)');
}