用户在 PHP 中提交的 header 文件名是否存在安全漏洞?

Is a user submitted header filename in PHP a security vulnerability?

我想动态生成一个 PHP 仅包含 md5 哈希字符串的可下载文本文件。

用法:index.php?filename=myhash.txt&hash=8145c8f6d42fda2c3cfdd1d091bcb331

$filename = isset($_GET["filename"]) ? $_GET["filename"] : "md5.txt";
$hash = (isset($_GET["hash"]) && preg_match('/^[a-f0-9]{32}$/', $_GET["hash"])) ? $_GET["hash"] : "";

header("Content-type: text/plain");
header("Content-Disposition: attachment; filename=$filename");
echo $hash;

我担心 header 中使用的 $filename 任何人都可以修改。这是安全漏洞吗?

据我所知,理论上你可以这样做;由于您实际上从未访问过服务器上的任何文件,因此没有人可以对您的服务器做任何不利的事情。但是,可能会为他们的 OS 提供无效的文件名,我不完全确定会发生什么。考虑到无论哪种方式都会弹出下载对话框,让他们当时输入文件名似乎比让他们事先输入更安全。

我有理由相信这没有任何安全问题,但插入一个关于证明否定有多难的一般免责声明。

您不使用文件名访问您服务器的文件系统,因此您不易受到文件包含攻击。


您限制了文件的内容,因此您的用户应该不会被诱骗下载脚本,但我们还是假设一下:

给定一种编程语言,在这种语言中,可以仅使用匹配 [a-f0-9] 的字符(这意味着没有空格)编写程序,并且可以使用其中的 32 个字符编写程序,那么攻击者可以制作一个 URL,它似乎在您的网站上(因此值得信赖),这会导致下载名为 something.xyz 的文件。 (对于 xyz 的值,它被注册为用该语言编写的脚本的文件扩展名。)这将导致攻击者的程序被下载到某个地方,用户可能会双击它并执行它。

这是一个不太可能的情况,现代操作系统非常擅长将下载的文件标记为需要 "Are you sure you want to execute this?" 警报,因此这是一个严重的边缘情况。

偏执狂,您可能希望将文件名限制为具有 .txt.md5 扩展名的文件名。


特殊字符可能是一个问题,但我想不出任何会成为安全风险的问题。例如,如果在文件名中输入了一个新行,那么 PHP 的现代版本可能会抛出如下内容:

Warning: Header may not contain more than a single header, new line detected in /Users/david/tmp/hfjkljiwe/index.php on line 4

… 并阻止脚本输出 content-disposition header。

不要 运行 old versions of PHP,安装您的安全修复版本!


从另一方面来说,浏览器不太可能接受他们正在写入的文件系统会摇摇欲坠的文件名。


也就是说,在编写安全软件时,偏执狂从未对任何人造成任何伤害(尤其是当它必须与其他软件交互时 — 软件存在错误!),因此添加对文件名中允许的字符进行一些限制并不是一个坏主意。