Nginx + PHP-FPM 的 PHP 选项“cgi.fix_pathinfo”真的很危险吗?

Tot*_*tor 63 security nginx fastcgi php-fpm

已经有一个 很大 谈论关于相对于一个安全问题cgi.fix_pathinfo与Nginx的(通常是PHP-FPM,快速CGI)使用PHP选项。

结果,默认的 nginx 配置文件曾经说:

# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini
Run Code Online (Sandbox Code Playgroud)

但是,现在,“官方”Nginx wiki声明无需禁用上述 PHP 选项即可正确处理 PATH_INFO。所以呢?

问题

  • 你能解释清楚是做cgi.fix_pathinfo什么的吗?(官方文档只是说:“有关 PATH_INFO 的更多信息,请参阅 CGI 规范”)
  • PHP 将真正用这些PATH_INFOSCRIPT_FILENAME变量做什么?
  • 为什么和如何使用 Nginx 会很危险?(详细例子)
  • 这些程序的最新版本是否仍然存在该问题?
  • Apache 易受攻击吗?

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

use*_*322 90

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

我将尝试解决您的具体问题,但是您对 PATH_INFO 的误解使问题本身有点错误。

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

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

    • 较早版本的PHP天真和技术上它甚至没有支持PATH_INFO,所以应该有什么要PATH_INFO被被改写的在SCRIPT_FILENAME其中,是的,在很多情况下被打破。我没有足够旧的 PHP 版本来测试,但我相信它SCRIPT_FILENAME在上面的例子中看到了整个shebang:“/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”。
  • 现在应该清楚为什么以及如何危险:

    • 网络服务器真的没有错——它只是将 URI 代理到 PHP,它无辜地发现“foo.jpg”实际上包含 PHP 内容,所以它执行它(现在你已经被 pwned!)。这是不是特别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
Run Code Online (Sandbox Code Playgroud)

同样,这在当今许多地方都是默认设置。

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

  • 不错的答案!澄清一下:如果如您所说,PHP 确定了 `SCRIPT_FILENAME` 是什么,为什么我的 `nginx` conf 中有一行 `fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;` 行?它是否覆盖了 PHP 自己发现 `SCRIPT_FILENAME` 值的努力? (6认同)

Hrv*_*jar 15

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

关于使用 php-fpm 和 unix socket 来避免它;海事组织它不会解决问题。

  • 这可能是真的,但你的标题有问题,这个问题的答案在我的回答中。如果你明确想要它;是的,这很危险;非常危险。 (7认同)