阻止人们通过表单上传恶意 PHP 文件

Posted

技术标签:

【中文标题】阻止人们通过表单上传恶意 PHP 文件【英文标题】:Stop people uploading malicious PHP files via forms 【发布时间】:2010-10-10 19:02:37 【问题描述】:

我在我的网站上用 php 创建了一个上传表单,人们可以上传一个 zip 文件。然后提取 zip 文件并将所有文件位置添加到数据库中。上传表格仅供人们上传图片,显然,文件位于 zip 文件夹中,在文件被提取之前我无法检查正在上传的文件。我需要一段代码来删除所有不是图像格式的文件(.png、.jpeg 等)。我真的很担心人们能够上传恶意php文件,安全风险很大!我还需要注意人们更改 php 文件的扩展名以试图绕过此安全功能。

这是我使用的原始脚本http://net.tutsplus.com/videos/screencasts/how-to-open-zip-files-with-php/

这是实际提取 .zip 文件的代码:

function openZip($file_to_open) 
    global $target;

    $zip = new ZipArchive();
    $x = $zip->open($file_to_open);
    if($x === true) 
        $zip->extractTo($target);
        $zip->close();

        unlink($file_to_open);
     else 
        die("There was a problem. Please try again!");
    

谢谢,本。

【问题讨论】:

【参考方案1】:

那么,您可能不应该只依赖文件扩展名。尝试通过图像库传递每个文件,以验证它是否真的是图像。

【讨论】:

您好,感谢您的快速回复!我以前从未听说过这种方法。我将如何通过图像库传递文件? 试用 PHP 的 WideImage。尝试根据文件名加载和验证图像。捕获发生的任何错误,您就会知道它是否至少是指定格式的有效图像。 @John:这么多年来,我什至从未考虑过这是一种解决方案。谢谢!【参考方案2】:

我没有看到在您的数据库中重命名 php 文件的风险... 只要您不将它们评估为 PHP 文件(或根本不评估它们),它们就不会造成太大的伤害,并且由于没有 .php 扩展名,php 引擎不会触及它们。

我猜你也可以在文件中搜索<?php...

另外:假设上传到您机器上的文件最坏。将保存它们的文件夹重命名为“病毒”并相应地对待它。不要公开,不要给任何文件启动权限(尤其是php用户)等等

【讨论】:

我不太担心输入数据库的内容,只是实际的 php 文件位于提取的文件夹中,以后有人可能会运行它。只要没有无法读取的php文件,我想那还不错。【参考方案3】:

现在您依靠硬盘空间进行提取。您可以检查文件头以确定它们是什么类型的文件。可能有图书馆。

offtopic:让用户选择几张图片而不是上传一个 zip 文件不是更好吗?更适合不知道 zip 是什么的人(是的,它们存在)

【讨论】:

嗨 PoweRoy,理想情况下,这就是我想要实现的。但它适用于拥有大量库存的人,我的用户需要很长时间才能单独上传每张图片。所以这是最好的方法,不会让我失去客户。您提到的这些库是什么?谢谢,本。 对于多个文件,我认为这在 web 开发中是可能的,我认为这不是。您可以尝试 java,但如果这是一个理想的解决方案...对于库,请参阅 John Ellinwood anser。【参考方案4】:

首先,您应该禁止所有没有正确图像文件扩展名的文件。之后,您可以使用getimagesize function 来检查文件是否为常规图像文件。

但您还应该注意,某些图像格式允许 cmets 和其他元信息。这可用于某些浏览器在某些情况下会执行的恶意代码(例如 javascript)(请参阅Risky MIME sniffing in Internet Explorer)。

【讨论】:

图片文件扩展名?如"myfile.php\u0000.png"? hhhh 不工作这是危险的! ha.ckers.org/blog/20070604/…【参考方案5】:

如果您将 php 设置为仅解析以 .php 结尾的文件,那么您只需将文件从 somename.php 重命名为 somename.php.jpeg 就可以了。

如果你真的想删除文件,php 可以使用zip library。您可以使用它来检查上传的 zip 存档中所有文件的名称和扩展名,如果它包含 php 文件,则给用户一个错误消息。

【讨论】:

【参考方案6】:

我真的很担心别人会上传恶意的php文件,安全隐患大!

冰山一角!

我还需要注意有人更改 php 文件的扩展名以试图绕过此安全功能。

通常更改扩展名会阻止 PHP 将这些文件解释为脚本。但这不是唯一的问题。除了“...php”之外,还有更多的东西会损坏服务器端; '.htaccess' 和设置了 X 位的文件是显而易见的,但绝不是您需要担心的。即使忽略服务器端的东西,也存在一个巨大的客户端问题。

例如,如果有人可以上传一个“.html”文件,他们可以在其中包含一个

另外,由于某些浏览器(主要是 IE)的“内容嗅探”行为,上传为“.gif”的文件实际上可能包含诸如此类的恶意 HTML。如果 IE 在文件开头附近看到类似(但不限于)“”之类的信息,它可以忽略提供的“Content-Type”并显示为 HTML,从而导致 XSS。

另外,可以制作一个文件,该文件 是您的图像解析器将接受的有效图像,并且 包含嵌入的 HTML。根据用户浏览器的确切版本和图像文件的确切格式(尤其是 JPEG 具有一组非常可变的可能标题格式),可能会出现各种结果。 mitigations 会在 IE8 中出现,但现在这没有用,你不得不想知道为什么他们不能简单地停止进行内容嗅探,你们这些白痴 MS,而不是让我们负担不起的 non - 对 HTTP 标头的标准扩展,一开始就应该使用。

我又陷入了咆哮。我会停下来。安全地提供用户提供的图像的策略:

1: 永远不要使用取自用户输入的文件名将文件存储在服务器的文件系统中。这可以防止错误和攻击:不同的文件系统对于文件名中允许哪些字符有不同的规则,而且“清理”文件名比您想象的要困难得多。

即使您使用了“仅 ASCII 字母”之类的非常严格的内容,您仍然需要担心名称太长、太短和保留名称:尝试使用像“com.txt”这样无害的名称来保存文件” 在 Windows 服务器上并观察您的应用程序出现故障。认为您知道您的应用程序可能在其上运行的每个文件系统的路径名的所有奇怪缺点吗?有信心吗?

相反,将文件详细信息(例如名称和媒体类型)存储在数据库中,并使用主键作为文件存储中的名称(例如“74293.dat”)。然后,您需要一种方法为它们提供不同的明显文件名,例如将文件吐出的下载器脚本、执行 Web 服务器内部重定向或 URL 重写的下载器脚本。

2:使用 ZipArchive 时要非常非常小心。在 extractTo 中存在相同类型的遍历 vulnerabilities,它们影响了大多数基于路径的 ZIP 提取器。此外,您还可以接受来自ZIP bombs 的攻击。最好通过逐步检查存档中的每个文件条目(例如使用zip_read/zip_entry_*)并检查其详细信息,然后手动将其流解压缩到您生成的具有已知良好名称和模式标志的文件,从而避免任何错误文件名的危险没有档案的帮助。忽略 ZIP 中的文件夹路径。

3:如果您可以加载图像文件并再次将其保存,尤其是如果您在两者之间以某种方式处理它(例如调整大小/缩略图,或添加水印)您可以合理地确定结果将是干净的。从理论上讲,有可能制作一个针对特定图像压缩器的图像,这样当它被压缩时,结果也会看起来像 HTML,但这对我来说似乎是一个非常困难的攻击。

4:如果您可以将所有图片作为下载内容提供(即在下载脚本中使用“Content-Disposition: attachment”),那么您可能是安全的。但这对用户来说可能太不方便了。不过,这可以与 (3) 协同工作,内联提供较小的、已处理的图像,并让原始的高质量图像仅作为下载提供。

5:如果您必须内联提供未更改的图像,则可以通过从不同域提供它们来消除跨站点脚本风险。例如,对不受信任的图像使用“images.example.com”,对包含所有逻辑的主站点使用“www.example.com”。确保 cookie 仅限于正确的虚拟主机,并且设置了虚拟主机,以便它们除了正确的名称之外无法响应任何内容(另请参阅:DNS 重新绑定攻击)。这就是许多网络邮件服务所做的。

总之,用户提交的媒体内容是个问题。

总结总结,AAAARRRRRRRGGGGHHH。

ETA 重新评论:

您在顶部提到“设置了 X 位的文件”,这是什么意思?

我不能代表ZipArchive.extractTo(),因为我还没有测试过它,但是许多提取器在被要求从存档中转储文件时,会重新创建与每个文件关联的 [一些] Unix 文件模式标志(如果存档是在 Unix 上创建的,因此实际上具有模式标志)。如果缺少所有者读取权限,这可能会导致您出现权限问题。但如果您的服务器启用了 CGI,这也可能是一个安全问题:X 位可以允许将文件解释为脚本并传递给第一行 hashbang 中列出的任何脚本解释器。

我认为 .htaccess 必须在主根目录中,不是这样吗?

取决于 Apache 的设置方式,尤其是 AllowOverride 指令。通用主机在任何目录上允许覆盖是很常见的。

如果有人仍然上传 ../var/www/wr_dir/evil.php 之类的文件会怎样?

我希望前导“..”会被丢弃,这就是遭受相同漏洞的其他工具所做的。

但我仍然不相信 extractTo() 会对抗恶意输入,有太多奇怪的小文件名/目录树可能出错 — 尤其是如果您希望在 Windows 服务器上运行。 zip_read() 让您可以更好地控制取消归档过程,从而减少攻击者。

【讨论】:

嗨 bobince,感谢您的出色回复。在顶部您提到“设置了 X 位的文件”,这是什么意思?而且,我认为 .htaccess 必须在主根目录中,不是这样吗?谢谢。 还有一个问题,ZipArchive 类的 extractTo 函数中存在漏洞(未扁平化的文件)。我认为我不必担心这个问题,因为我使用的是 php 版本 5.2.8 和修复程序是在版本 5.2.7 中吗?如果有人仍然上传像 ../var/www/wr_dir/evil.php 这样的文件会发生什么?【参考方案7】:

您可能还想考虑使用以下库进行 mime 类型检测:

http://ca.php.net/manual/en/ref.fileinfo.php

【讨论】:

【参考方案8】:

就我个人而言,我会在 Apache 配置中添加一些内容,以确保它将 PHP 文件作为文件上传到的位置的文本提供,因此您很安全,并且可以允许其他文件类型在未来。

【讨论】:

【参考方案9】:

注意这个通过 getimagesize() 传递恶意 PHP

通过试图确保图像的图像函数注入 PHP 使用 getimagesize() 函数是安全的

在此处阅读更多信息http://ha.ckers.org/blog/20070604/passing-malicious-php-through-getimagesize/

更好地为您的用户徽标使用 *** 使用的 gravatar ;)

【讨论】:

【参考方案10】:

使用 getimagesize 函数。 完整程序:- 1.) 提取图像/上传文件的扩展名,然后将扩展名与允许的扩展名进行比较。 2.) 现在创建一个用于重命名上传文件的随机字符串。最好的想法是md5(session_id().microtime())。它不能被复制,并且如果你的服务器非常快并且可以处理不到一微秒而不是使用递增变量并将它们添加到字符串中。 现在移动那个文件。

提示 在上传目录中禁用 PHP 文件处理,它将始终防止您受到任何服务器端攻击,如果可能的话,将您的 htaccess 添加到根目录或 httpd 配置文件中并从那里禁用 htaccess 文件现在它可以解决您的最大问题

【讨论】:

以上是关于阻止人们通过表单上传恶意 PHP 文件的主要内容,如果未能解决你的问题,请参考以下文章

由于 phpunit 恶意文件 eval-stdin.php,端口 80 在 cpanel 上被阻止

在 PHP 中阻止图像主机的隐写术?

无法通过 onSubmit 属性触发的 Ajax 响应阻止表单提交。

PHP 阻止Wordpress上的恶意URL请求

阻止人们破解基于 PHP 的 Flash 游戏高分表的最佳方法是啥

即使重命名其扩展名,也阻止上传 exe