对于 Nginx + PHP-FPM 来说,PHP 选项“cgi.fix_pathinfo”真的危险吗?

对于 Nginx + PHP-FPM 来说,PHP 选项“cgi.fix_pathinfo”真的危险吗?

已经有A 很多 关于cgi.fix_pathinfo与 Nginx 一起使用的 PHP 选项(通常是 PHP-FPM、快速 CGI)相关的安全问题。

因此,默认的 nginx 配置文件是这样的:

# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

然而,现在,“官方” Nginx wiki指出无需禁用上述 PHP 选项,PATH_INFO 即可正确处理。那又怎么样?

问题

  • 你能清楚地解释一下cgi.fix_pathinfo做什么吗?(官方文件只是说:“有关 PATH_INFO 的更多信息,请参阅 CGI 规范”)
  • PATH_INFOPHP 实际上会用这些和变量做什么SCRIPT_FILENAME
  • Nginx 为何以及如何会带来危险?(详细的例子)
  • 这些程序的最新版本中是否仍然存在该问题?
  • Apache 是否存在漏洞?

我试图理解每一步的问题。例如,我不明白为什么使用 php-fpm Unix 套接字可以避免这个问题。

答案1

TL;DR - 修复(您甚至可能不需要)非常简单,并且位于本答案的末尾。

我会尽力解答您的具体问题,但是您对 PATH_INFO 的误解使得问题本身有点错误。

  • 第一个问题应该是“这个路径信息业务是什么?”

  • 您的下一个问题应该是:“PHP 如何确定什么是PATH_INFOSCRIPT_FILENAME是什么?”

    • 早期版本的 PHP 很幼稚,从技术上讲甚至不支持PATH_INFO,所以本来应该PATH_INFO被混杂在一起SCRIPT_FILENAME,是的,在许多情况下是错误的。我没有足够老的 PHP 版本来测试,但我相信它认为它是SCRIPT_FILENAME完整的:上例中的“/path/to/script.php/THIS/IS/PATH/INFO”(像往常一样以 docroot 为前缀)。
    • 启用 cgi.fix_pathinfo 后,PHP 现在可以正确找到上述示例中的“/THIS/IS/PATH/INFO”并将其放入PATH_INFOSCRIPT_FILENAME获取指向所请求脚本的部分(当然以 docroot 为前缀)。
    • 注意:当 PHP 开始真正支持时PATH_INFO,他们必须为新功能添加配置设置,以便运行依赖于旧行为的脚本的用户可以运行新版本的 PHP。这就是为什么甚至有一个配置开关。它应该从一开始就内置(具有“危险”行为)。
  • 但是 PHP 如何知道哪个部分是脚本以及它的路径信息是什么?如果 URI 是这样的:

    http://example.com/path/to/script.php/THIS/IS/PATH/INFO.php?q=foo

    • 在某些环境中,这可能是一个复杂的问题。在 PHP 中,它会发现 URI 路径的第一部分与服务器的 docroot 下的任何内容都不对应。对于此示例,它会发现您的服务器上没有“/docroot/path/to/script.php/THIS”,但您肯定有“/docroot/path/to/script.php”,因此现在SCRIPT_FILENAME已经确定并PATH_INFO获取其余部分。
    • 现在,Nginx 文档和Hrvoje Špoljar 的回答(你不能对这样一个清晰的例子吹毛求疵)变得更加清晰:给出 Hrvoje 的例子(“http://example.com/foo.jpg/nonexistent.php"),PHP 在您的 docroot 上看到一个文件“/foo.jpg”,但是它没有看到任何名为“/foo.jpg/nonexistent.php”的东西,因此SCRIPT_FILENAME获取“/foo.jpg”(再次以 docroot 为前缀)并PATH_INFO获取“/nonexistent.php”。
  • 现在我们应该已经清楚它为何以及如何会造成危险了:

    • Web 服务器其实没有错——它只是将 URI 代理到 PHP,PHP 无意中发现“foo.jpg”实际上包含 PHP 内容,因此执行它(现在你被黑了!)。这是不是特别是 Nginx 本身。
  • 真实的问题在于,您允许不受信任的内容在未经清理的情况下被上传到某处,并且允许其他任意请求到达同一位置,而 PHP 在可以时会愉快地执行这些请求。
  • Nginx 和 Apache 可以构建或配置为阻止使用此技巧的请求,并且有很多关于如何做到这一点的示例,包括user2372674 的回答这篇博客文章很好地解释了问题,但缺少正确的解决方案。

  • 但是,最好的解决方案是确保 PHP-FPM 配置正确,这样它就不会执行以“.php”结尾的文件。值得注意的是,PHP-FPM 的最新版本(~5.3.9+?)将此作为默认设置,因此这种危险不再是大问题。

解决方案

如果您拥有最新版本的 PHP-FPM(~5.3.9+?),那么您不需要执行任何操作,因为下面的安全行为已经是默认的。

否则,找到 php-fpm 的www.conf文件(可能/etc/php-fpm.d/www.conf,取决于您的系统)。确保您有这个:

security.limit_extensions = .php

再说了,如今这在很多地方都是默认的。

请注意,这并不能阻止攻击者将“.php”文件上传到 WordPress 上传文件夹并使用相同技术执行该文件。您仍然需要为您的应用程序提供良好的安全性。

答案2

本质上没有这个,你可以将名为“foo.jpg”的php代码文件上传到网络服务器;然后像http://domain.tld/foo.jpg/nonexistent.php并且 Web 服务器堆栈会错误地说“哦;这是一个 PHP;我需要处理它”,它将无法找到 foo.jpg/nonexistent.php,因此它将返回到 foo.jpg 并将 foo.jpg 作为 php 代码处理。这很危险,因为它使系统很容易受到入侵;例如,任何允许上传图像的 Web 应用程序都会成为上传后门的工具。

关于使用带有 unix 套接字的 php-fpm 来避免这个问题;在我看来它并不能解决问题。

答案3

在里面Nginx 维基百科作为安全措施

if (!-f $document_root$fastcgi_script_name) {
    return 404;
}

包含在位置块中。在其他教程中

try_files $uri =404;

,它应该做同样的事情,但根据 Nginx wiki 的说法,它会产生问题。使用这些选项,cgi.fix_pathinfo=1应该不会再有问题了。更多信息可以找到这里

相关内容