与Nginx所使用的cgi.fix_pathinfo PHP选项(通常是PHP-FPM,快速CGI)有关的安全问题已经引起了很多讨论。结果,默认的nginx配置文件通常说:无需禁用上述PHP选项,即可正确处理PATH_INFO。那是什么?

问题


您能清楚解释cgi.fix_pathinfo的作用吗? (官方文档只是说:“有关PATH_INFO的更多信息,请参见CGI规范。”)
PHP会对这些PATH_INFOSCRIPT_FILENAME变量真正起作用吗?
为什么使用Nginx会有危险? (详细示例)
这些程序的最新版本是否仍然存在该问题?
Apache容易受到攻击吗?例如,我不明白为什么使用php-fpm Unix套接字可以避免此问题。

评论

您可以通过了解PATH_INFO和PATH_TRANSLATED的区别来回答自己的问题:blogs.msdn.com/b/david.wang/archive/2005/08/04/…

#1 楼

TL; DR-解决方案(您甚至可能不需要)非常简单,并且在此答案的结尾。使问题本身有点错误。



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



路径信息是URI脚本之后的内容(应以正斜杠开头,但在查询参数之前以?开头)。 Wikipedia文章概述部分中有关CGI的最后一段很好地总结了这一点。 PATH_INFO下方是“ / THIS / IS / PATH / INFO”:

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




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


较早的PHP版本太幼稚,从技术上讲甚至不支持PATH_INFO,因此本来应该是PATH_INFO的内容被替换到SCRIPT_FILENAME上,是的,在很多情况下,它已损坏。我没有足够老的PHP版本可以测试,但我相信它把SCRIPT_FILENAME视为整个示例:在上面的示例中为“ /path/to/script.php/THIS/IS/PATH/INFO”(前缀
启用cgi.fix_pathinfo后,PHP现在可以为上述示例正确找到“ / THIS / IS / PATH / INFO”,并将其放入PATH_INFO中,而SCRIPT_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组成。更清楚:给定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,它无辜地发现“ foo.jpg”实际上是包含PHP内容,因此它将执行它(现在已被伪装!)。这不是Nginx本身所特有的。


真正的问题是,您可以将未经信任的内容上传到某处而不进行消毒,并且允许将其他任意请求发送到同一位置,PHP会幸福地执行
可以构建或配置Nginx和Apache来阻止使用这种欺骗手段的请求,并且有很多示例说明如何做到这一点,包括在user2372674的答案中。这篇博客文章很好地解释了这个问题,但是缺少正确的解决方案。
但是,最好的解决方案是仅确保正确配置PHP-FPM,以便它永远不会执行文件,除非文件以“ .php”结尾。值得注意的是,最新版本的PHP-FPM(〜5.3.9 +?)将此默认设置为默认值,因此这种危险不再是太大的问题了。 br />如果您具有最新版本的PHP-FPM(〜5.3.9 +?),则无需执行任何操作,因为以下安全行为已是默认设置。 php-fpm的www.conf文件(也许是/etc/php-fpm.d/www.conf,取决于您的系统)。请确保您具有以下内容:

security.limit_extensions = .php


再次出现,这在很多地方都是默认设置。从将“ .php”文件上传到WordPress上载文件夹,然后使用相同的技术执行该文件。您仍然需要为应用程序提供良好的安全性。

评论


好答案!需要说明的是:如果您说的那样,PHP是否确定了SCRIPT_FILENAME是什么,为什么会有fastcgi_param SCRIPT_FILENAME $ document_root $ fastcgi_script_name;我的nginx conf中的行?它会否覆盖PHP自己发现SCRIPT_FILENAME值的工作?

– Tortor
16-11-5在1:09



是否有一个函数来获取security.limit_extensions的值?我尝试phpinfo(),ini_get(security.limit_extensions)和ini_get_all()没有成功。

–肘部龙虾
18年6月28日在13:31

谢谢,如果最新版本的PHP-FPM(〜5.3.9 +?)将此默认为默认值,为什么php7.1需要它?还是这篇文章错了?

–叶夫根尼·阿法纳舍夫(Yevgeniy Afanasyev)
18年11月27日在4:48

#2 楼

本质上,没有此功能,您可以将名为“ foo.jpg”的php代码文件上传到Web服务器。然后像http://domain.tld/foo.jpg/nonexistent.php那样请求它,Web服务器堆栈会错误地说哦;这是一个PHP;我需要处理此问题,它将找不到foo.jpg / nonexistent.php,因此它将退回到foo.jpg并将foo.jpg作为php代码进行处理。这很危险,因为它会使系统很容易受到入侵。

关于将php-fpm与unix套接字一起使用以避免这种情况,例如,任何允许图像上传的Web应用程序都将成为上传后门的工具。 IMO不能解决问题。

评论


您只重复在我提供的链接上可以阅读的内容。您没有解释真正的机制。您的答案需要增值恕我直言。

– Tortor
2014-09-12 13:23

可能是正确的,但是您的标题中有问题,我的答复是对该问题的解答。如果您明确想要它;是的,这很危险;非常危险。

– HrvojeŠpoljar
2014年9月12日下午13:43

1 /我的答案不仅限于标题:它有一个主体。 2 / user109322已经证明您错了:用于cgi.fix_pathinfo的任何值都不危险,因为php-fpm的默认conf是安全的(它将仅执行扩展名为.php的文件)。

– Tortor
16年11月5日,0:56

#3 楼

在Nginx Wiki中,作为安全措施

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


包含在位置块中。在其他教程中,使用的是
try_files $uri =404;


,应该这样做,但根据Nginx Wiki可能会出现问题。使用这些选项,cgi.fix_pathinfo=1应该不再是问题。更多信息可以在这里找到。