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

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

因此,默认的nginxconfiguration文件用来说:

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

然而,现在,“官方”Nginx维基指出, PATH_INFO可以正确处理,而不禁用上述的PHP选项。 所以呢?

问题

  • 你能清楚地解释一下cgi.fix_pathinfo做什么的? ( 官方文档只是说 :“有关PATH_INFO的更多信息,请参阅CGI规范”)
  • PHP会如何处理这些PATH_INFOSCRIPT_FILENAMEvariables?
  • 为什么以及如何对Nginx危险? ( 详细的例子)
  • 这些程序的最新版本中是否还存在这个问题?
  • Apache是​​脆弱的吗?

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

    TL; DR – 修复(你可能甚至不需要)很简单,并在这个答案的结尾。

    我会尽量解决你的具体问题,但是你对PATH_INFO的误解会使问题本身有点错误。

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

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

      • PHP的早期版本是天真的,技术上甚至不支持PATH_INFO ,所以应该是PATH_INFO被压缩到SCRIPT_FILENAME上,在很多情况下,这是被破坏的。 我没有一个足够老的PHP版本来testing,但我相信它看到SCRIPT_FILENAME作为整个版本:“/path/to/script.php/THIS/IS/PATH/INFO”在上面的例子(前缀与往常一样的文件根据)。
      • 启用cgi.fix_pathinfo后,PHP现在可以正确地find上述示例的“/ THIS / IS / PATH / INFO”,并将其放入PATH_INFOSCRIPT_FILENAME只获取指向所请求的脚本的部分(当然前缀为docroot) 。
      • 注意:当PHP开始实际支持PATH_INFO ,他们必须为新function添加一个configuration设置,以便运行依赖于旧行为的脚本的用户可以运行新的PHP版本。 这就是为什么甚至有一个configuration开关。 应该从一开始就内置(与“危险的”行为)。
    • 但是PHP如何知道脚本的哪部分以及它的path信息呢? 如果URI是这样的:

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

      • 在某些环境中这可能是一个复杂的问题。 PHP中会发生什么,它发现URIpath的第一部分不对应于服务器的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在你的文档根目录”/foo.jpg“上看到一个文件,但没有看到任何名为”/foo.jpg/nonexistent.php“的文件,所以SCRIPT_FILENAME得到”/foo.jpg “(再次,以docroot为前缀), PATH_INFO得到”/nonexistent.php“。
    • 现在应该清楚:为什么以及如何危险?

      • Web服务器真的没有错 – 它只是代理到PHP的URI,它无辜地发现“foo.jpg”实际上包含PHP内容,所以它执行它(现在你已经p了!)。 这对Nginx本身并不特别。
    • 真正的问题是,你让不信任的内容上传到某个地方,而没有消毒,你允许其他任意请求到同一个位置,PHP可以愉快地执行。
    • Nginx和Apache可以使用这个技巧来构build或者configuration为阻止请求,并且有很多例子可以解决这个问题,包括user2372674的答案 。 这篇博客文章很好地解释了这个问题,但是缺less正确的解决scheme。

    • 但是,最好的解决scheme是确保PHP-FPMconfiguration正确,以便永远不会执行文件,除非以“.php”结尾。 值得注意的是,最近版本的PHP-FPM(〜5.3.9 +?)将其作为默认值,所以这个危险不再是那么严重了。

    解决scheme

    如果你有最新版本的PHP-FPM(〜5.3.9 +?),那么你不需要做任何事情,因为下面的安全行为已经是默认了。

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

     security.limit_extensions = .php 

    再次,这是目前很多地方的默认设置。

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

    本质上,如果没有这个,你可以上传一个名为“foo.jpg”的php代码到web服务器上; 然后请求它像http://domain.tld/foo.jpg/nonexistent.php和Web服务器堆栈将错误地说哦; 这是一个PHP; 我需要处理这个,它将无法findfoo.jpg / nonexistent.php,所以它会回落到foo.jpg并将foo.jpg处理为php代码。 这是非常危险的,因为它打开系统很容易入侵; 任何允许图像上传的networking应用程序成为上传后门的工具。

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

    在Nginx wiki中作为安全措施

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

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

     try_files $uri =404; 

    被使用,这应该做同样的事情,但可以给根据Nginx维基的问题。 有了这些选项, cgi.fix_pathinfo=1应该不再是问题了。 更多信息可以在这里find。