有没有办法检查使用 PHP 上传的文件的文件类型?

信息安全 php 上传文件 文件类型
2021-09-08 19:53:19

我不希望它只检查文件的扩展名,因为这些很容易伪造,即使 MIME 类型可以使用 TamperData 等工具伪造。

那么有没有更好的方法来检查 PHP 中的文件类型?

4个回答

您需要 PHP 的Fileinfo函数,它们是 Unix 'file' 命令的 PHP 道德等价物。

请注意,输入文件充其量只是一个模糊的区域。瞄准白名单(“这个小类型的集合没问题”)而不是黑名单(“没有exes,没有dll,没有......”)。不要依赖文件类型作为抵御恶意文件的唯一防御措施。

文件中嵌入了签名或“幻数”,通常位于文件开头附近。libmagic 是一个提取文件签名并在签名数据库中查找的库。

这是 Unix 类型系统确定文件类型的方式,即如果您在 Linux 上保存一个没有扩展名的文本文件,它仍然会使用文本编辑器自动打开。

另一方面,像 Windows 这样的系统只查看文件扩展名。在 Windows 上打开没有扩展名的文本文件将导致 WTf-is-this 弹出窗口。

因此,检查扩展名和幻数是有好处的,因为您的网站可能会有使用不同操作系统的访问者。

没有文件类型的概念。在计算机世界中,一切都是一堆 0/1,它是图像还是许多随机字符取决于你如何解释你的零和一。文件类型(作为 .docx、.png 之类的扩展名)只是为了方便用户能够对它可能是什么进行有根据的猜测并使用适当的工具打开它。与任何猜测一样,它可能是错误的。

因此,与其尝试使用建议的fileinfo之类的技术,如果我是你,我宁愿弄清楚我允许人们上传什么。

因此,如果您允许人们上传图片,请使用getimagesize甚至可能检查宽度高度是否在适当的范围内(谁知道可能有人会上传像 500.000 像素宽度/高度这样的图片,而您的服务器会在调整大小时死机。它一个有效的图像,但仍然不是你想要的)。调整每个图像的大小并仅提供调整大小的格式并将其存储在无法触及的原始位置是否有意义。

如果您决定用户可以上传 .mp3 文件,请查看可以处理此类文件的内容谁知道可能已经有测试方法来检查这是否真的是 mp3 文件。

关于您的决定,请使用某些方法来缓解可能出现的问题(假设该人上传了文件$file = $_FILES['file'])

  • 在上传过程中检查错误 if (!$file['name'] || $file['error']){ return false; }
  • 检查此文件是否确实具有您接受的大小 if ($file['size'] > MaxPossible || $file['size'] < MinPossible){ return false; }
  • 重命名文件(如果我提交类似../../../t.py.png的内容,它将被重命名为uniquefilename.png
  • 它以尽可能少的权限保存。肯定没有权限执行。(可能是 640 或 660)
  • 确保没有办法执行 XSS,从单独的域保存和提供它们。

还包含 mime 类型,$_FILES您可以检查一下。

您可以使用特定的解析器解析文件,当文件不是真正等待的文件时,该解析器会引发异常......我认为其他任何事情都可以被伪造。
例如,您可以通过图像文件使用 GD 或 Imagick,通过 json 文件使用 JSON 解析器,通过 HTML 和 XML 文件使用 DOM 和 XML 解析器(关闭外部实体)等等......通过 Imagick,您也可以使用识别工具. 我认为对于其他文件类型还有其他工具。

文件上传真正重要的是

  • 阻止执行(chmod()用于更改文件属性,和/或将它们移动到静态子域。),
  • 文件包含(永远不要include通过服务客户端上传文件,使用文件读取方法file_get_contents(),或者使用X-Sendfile没有 HTTP 头注入漏洞的头,如果你想对文件进行访问控制。如果没有,那么让 HTTP 服务器完成它的工作.),
  • eval 注入(永远不要exif在上下文中使用数据eval,例如使用preg_replace().),
  • Content-Disposition内容嗅探(使用没有 HTTP 标头注入漏洞的标头强制下载Strict-Transport-Security或通过包含使用以下标头X-Content-Type-Options: 、、、、、 X-Frame-OptionsX-XSS-ProtectionContent-Security-Policy
  • xss(与内容嗅探相同。如果没有必要,请尽量避免包含客户端文件,并使用正确的标头。)

等等...

我在stackoverflow上写了一个关于PHP上传的更详细的答案,也许它有帮助。