黑客使用图片上传将 PHP 代码导入我的网站

信息安全 php 上传文件 验证
2021-08-25 01:18:33

我在一个网站上工作——现在它处于测试的早期阶段,还没有推出,只有测试数据——谢天谢地。

首先,黑客找到了登录网站“管理”页面的密码*我认为他们在朋友的计算机上使用了一个键盘记录器,该记录器登录到该网站给我反馈。

其次,他们使用图片上传框上传PHP文件。我进行了严格的检查,只接受 .jpg 和 .png 文件——其他所有文件都应该被拒绝。当然没有办法上传 .jpg 文件,然后在文件存储后更改扩展名?

值得庆幸的是,当文件发送给我时,我还会生成新的文件名,所以我认为他们无法找到文件来执行代码。

我似乎无法弄清楚该网站如何让 PHP 文件通过。我的安全出了什么问题?验证函数代码如下:

function ValidateChange_Logo(theForm)
{
   var regexp;
   if (theForm.FileUpload1.value == "")
   {
      alert("You have not chosen a new logo file, or your file is not supported.");
      theForm.FileUpload1.focus();
      return false;
   }
   var extension = theForm.FileUpload1.value.substr(theForm.FileUpload1.value.lastIndexOf('.'));
   if ((extension.toLowerCase() != ".jpg") &&
       (extension.toLowerCase() != ".png"))
   {
      alert("You have not chosen a new logo file, or your file is not supported.");
      theForm.FileUpload1.focus();
      return false;
   }
   return true;
}

文件到达服务器后,我使用以下代码保留扩展名,并生成一个新的随机名称。它有点乱,但效果很好。

// Process and Retain File Extension
$fileExt = $_FILES[logo][name];
$reversed = strrev($fileExt);
$extension0 = substr($reversed, 0, 1);
$extension1 = substr($reversed, 1, 1);
$extension2 = substr($reversed, 2, 1);
$fileExtension = ".".$extension2.$extension1.$extension0;
$newName = rand(1000000, 9999999) . $fileExtension;

我刚刚测试了一个名称,例如logo.php;.jpg,虽然图片无法被网站打开,但它正确地将名称更改为123456.jpg. 至于logo.php/.jpg,Windows 不允许这样的文件名。


* 网站上允许简单功能的受保护页面:例如上传一张图片,然后成为网站的新徽标。FTP 详细信息与用于登录网站上受保护页面的密码完全不同。数据库和 cPanel 凭据也是如此。我确保人们甚至无法查看该站点的文件夹和文件结构。如果您没有 FTP 详细信息,我根本无法想到在此站点上将 .jpg 或 .png 扩展名重命名为 .php。

4个回答

客户端验证

您提供的验证代码是 JavaScript。这表明它是您用来在客户端上进行验证的代码。

保护 webapps 的第一条规则是永远不要信任客户端客户端处于用户的完全控制之下——或者在这种情况下是攻击者。您无法确定您发送给客户端的任何代码都用于任何用途,并且您在客户端上放置的任何块都没有任何安全价值。客户端验证只是为了提供流畅的用户体验,而不是实际执行任何与安全相关的约束。

攻击者可以在他们的浏览器中更改那段 JavaScript,或者完全关闭脚本,或者只是不从浏览器发送文件,而是使用 curl 等工具制作自己的 POST 请求。

您需要重新验证服务器上的所有内容。这意味着您的 PHP 必须检查文件的类型是否正确,而您当前的代码却没有。

如何做到这一点是一个广泛的问题,我认为超出了您的问题范围,但这个问题是开始阅读的好地方。您可能还想查看您的.htaccess文件。

获取扩展

也许不是安全问题,但这一种更好的方法:

$ext = pathinfo($filename, PATHINFO_EXTENSION);

神奇的 PHP 函数

当我从编辑框存储数据时,我使用 PHP 的所有三个函数来清理它:

$cleanedName = strip_tags($_POST[name]); // Remove HTML tags
$cleanedName = htmlspecialchars($cleanedName); // Allow special chars, but store them safely. 
$cleanedName = mysqli_real_escape_string($connectionName, $cleanedName);

这不是一个好的策略。您在验证文件扩展名的上下文中提到这一点让我担心您只是在输入时抛出了几个与安全相关的函数,希望它能解决问题。

例如,您应该使用准备好的语句而不是转义来防止 SQLi。从数据库中检索数据后,需要对 HTML 标记和特殊字符进行转义以防止 XSS,具体如何操作取决于您插入该数据的位置。等等等等。

结论

我并不是说这是刻薄的,但是您似乎在这里犯了很多错误。如果还有其他漏洞,我不会感到惊讶。如果您的应用程序处理任何敏感信息,我强烈建议您在将其投入生产之前让具有安全经验的人查看它。

首先,如果有人利用了文件上传功能,请确保您在客户端 服务器上都验证了文件。绕过客户端 JavaScript 保护是微不足道的。

其次,确保您通过并实施来自 OWASP 的这些想法

这应该涵盖了您的大部分问题。还要确保您的服务器上没有任何其他未修补的缺陷(例如,过时的插件、可利用的服务器等)

在这里需要特别注意的是,PHP 旨在嵌入到其他内容中。具体来说,它被设计为嵌入到 HTML 页面中,用少量动态更新的数据来补充大部分静态页面。

而且,很简单,它并不关心它嵌入了什么PHP 解释器将扫描任意文件类型,并执行任何用脚本标签正确标记的 PHP 内容。

这导致的历史问题是,是的,图像上传可以包含 PHP。如果 Web 服务器愿意通过 PHP 解释器过滤这些文件,那么该代码将执行。请参阅 Wordpress“Tim Thumb”插件惨败。

正确的解决方案是确保您的 Web 服务器永远不会将不受信任的内容视为应该通过 PHP 运行的内容。尝试过滤输入是一种解决方法,但正如您所发现的那样,它有限且容易出错。

更好地验证文件位置和文件扩展名都用于定义 PHP 将处理什么,并将上传和其他不受信任的内容隔离到不会被处理的位置和扩展名中。(您如何做到这一点因服务器而异,并且遭受“使事情正常工作通常意味着以您不知道的方式放松安全性”的问题。)

您可以通过 .htaccess 在您的上传目录中禁用 PHP,这样您的服务器就不会在该目录及其子目录中执行任何 PHP 代码。

使用以下规则在您的上传目录中创建一个 .htaccess 文件:

php_flag engine off

请注意,.htaccess 规则仅在您的 PHP 服务器在mod_php模式下运行时才有效。

编辑:当然,我忘了提到 .htaccess 仅在 apache 中有效。