我花了好几个小时才弄清楚如何正确配置NginX
,但我仍然认为我可能错过了一些东西。主要是因为这个完美工作的设置引发的问题比它解答的问题还多。也许这甚至是一个-bug 或中NginX
引入的东西,我根本不知道。Debian
Version: 1.2.1-2.2+wheezy2
Package: nginx-extras
请注意,我的示例已被精简到最低限度(因此以此为起点),但它仍然包含所有必要的当前安全修复。
请注意,我不喜欢容易出错的启发式方法。因此,这将使用/etc/php/fpm/pool.d/www.conf
如下设置运行,它php-fpm
直接从中获取脚本,PATH_TRANSLATED
而不是猜测它:
php_admin_value[cgi.fix_pathinfo]=0
security.limit_extensions = .php
php_admin_flag[expose_php] = off
这是转储变量的php
文件:/home/tino/www/index.php
<?
header("content-type: text/plain");
$a = array("PATH_TRANSLATED", "SCRIPT_FILENAME", "DOCUMENT_URI", "PATH_INFO", "QUERY_STRING" );
foreach ($a as &$v)
printf("%-15s %s\n", $v, $_SERVER[$v]);
?>
工作 NginX 配置
请注意,这/home/tino/www
就是我的测试脚本所在位置。您可能希望从中填充所有其他参数/etc/nginx/fastcgi_params
。例如,您需要
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
填充PHP_SELF
。
server_tokens off;
server {
listen 80;
root /home/tino/www;
if ($request_uri ~ " ") { return 444; }
location ~ [^/]\.php(/|$) {
limit_except GET HEAD POST { deny all; }
fastcgi_split_path_info ^((?U).+\.php)(.*)$;
try_files $fastcgi_script_name =404;
set $wtf $fastcgi_path_info;
fastcgi_param PATH_INFO $wtf;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
}
}
这给了我以下输出http://example.com/index.php/a.php?b=b.php
PATH_TRANSLATED /home/tino/www/index.php
SCRIPT_FILENAME /home/tino/www/index.php
DOCUMENT_URI /index.php
PATH_INFO /a.php
QUERY_STRING b=b.php
这正是我想要的。当然,请注意,这也适用于问题较少的 URI。
不起作用的配置
然而,直接配置不起作用,我真的很困惑,为什么:
server_tokens off;
server {
listen 80;
root /home/tino/www;
if ($request_uri ~ " ") { return 444; }
location ~ [^/]\.php(/|$) {
limit_except GET HEAD POST { deny all; }
fastcgi_split_path_info ^((?U).+\.php)(.*)$;
try_files $fastcgi_script_name =404;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
}
}
这与上面的相同,只是$wtf
缺少解决方法:
set $wtf $fastcgi_path_info;
fastcgi_param PATH_INFO $wtf;
成为
fastcgi_param PATH_INFO $fastcgi_path_info;
但这不起作用,目前的输出http://example.com/index.php/a.php?b=b.php
是:
PATH_TRANSLATED /home/tino/www/index.php
SCRIPT_FILENAME /home/tino/www/index.php
DOCUMENT_URI /index.php
PATH_INFO
QUERY_STRING b=b.php
如您所见:PATH_INFO
消失了。这与 无关php-fpm
,它纯粹是一个NginX
事物!
不安全的配置再次起作用
配置完成后,这将带来重大的安全漏洞由于缺少文件检查,再次运行:
server_tokens off;
server {
listen 80;
root /home/tino/www;
if ($request_uri ~ " ") { return 444; }
location ~ [^/]\.php(/|$) {
limit_except GET HEAD POST { deny all; }
fastcgi_split_path_info ^((?U).+\.php)(.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param REQUEST_METHOD $request_method;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_FILENAME $request_filename;
fastcgi_param DOCUMENT_URI $document_uri;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
}
}
但结果有一点不同http://example.com/index.php/a.php?b=b.php
(SCRIPT_FILENAME
看起来是错误的,DOCUMENT_URI
但从不同的角度来看可能是正确的)。
PATH_TRANSLATED /home/tino/www/index.php
SCRIPT_FILENAME /home/tino/www/index.php/a.php
DOCUMENT_URI /index.php/a.php
PATH_INFO /a.php
QUERY_STRING b=b.php
所以,请
显然,在使用设置正则表达式后,解析try_files
方式会中断。$fastcgi_path_info
fastcgi_split_path_info
所以,请问有人能告诉我,这种$wtf
解决方法是否确实是正确的解决方案,或者我是否仍然需要担心其他不良副作用try_files
?
谢谢。(如果我遗漏了一些重要的安全信息,请评论。)
答案1
昨天我正在使用 PHP-FPM 设置我的第一个 Nginx 服务器,我可能遇到了和你一样的问题。长话短说,我无法正确安全地设置 Nginx,并且所有 PHP 的服务器变量都正确设置。我终于找到了一个合适的设置,我将与你分享。但是,如果能从 Nginx 专家那里得到关于这个问题的意见,那就太好了。说真的,我们遇到了一些本来不应该发生的非常奇怪的问题。
您的设置问题
因此,基本上,您需要一个可工作且安全的 Nginx+PHP-FPM 安装。我说的“可工作”是指 PHP 的服务器变量已正确设置,我说的“安全”是指 Nginx 已受到保护,免受已知漏洞的侵害。
第一的of,我能够重现您的配置和问题。出于某种原因,正在try_files
破坏某些东西。我无法说出原因,但您说得对。try_files
用以下代码替换指令似乎可以解决该问题:
if (!-f $document_root$fastcgi_script_name) {
return 404;
}
编辑:正如下面评论中讨论的那样,上面的代码应该避免。而且,原始try_files
指令正在按预期发挥作用。
第二,我注意到您没有包含fastcgi_params
负责设置所有需要的(和必需的)FastCGI 参数的文件,这些参数将用于设置 PHP 的服务器变量。我不知道您为什么不包含此文件,因为我在网上读到的每一篇文档都告诉我们要使用该文件。现在文件已包含,我们需要修改一些默认情况下未正确设置的参数。最后,我们需要添加以下指令:
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
注意SCRIPT_FILENAME
与您的有何不同。同样,我读过很多文档,它们告诉我们要使用此值(我将在下面提供链接)。您可以将指令fastcgi_param
直接放在fastcgi_params
文件中,也可以放在您的location
部分中。
然后,什么都不起作用。如果我使用和你相同的链接,我得到的是No input file specified
。查看日志文件,我得到的是Unable to open primary script: /var/www/a.php (No such file or directory)
。我不知道为什么会这样,但所有的 fastcgi 参数似乎都混淆了。
现在,最奇怪的是,如果我删除这一行:
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
它又能正常工作了,但 PHP 的服务器变量混淆了。不仅PATH_TRANSLATED
没有设置,PHP_SELF
而且其他变量也没有正确设置。这是我长期以来一直苦苦挣扎的地方。幸运的是,我找到了一个似乎符合我们目标的解决方案。
工作设置
如果我们在 PHP 配置中重新启用cgi.fix_pathinfo
,一切就都正常了。起初,我认为这样做是个坏主意。但是,我阅读了cgi.fix_pathinfo
启用后的安全风险,才发现我们已经受到了保护。默认情况下security.limit_extensions
设置为.php
,我们就可以免受cgi.fix_pathinfo
问题的影响。此外,文件存在性检查(if
我们之前添加的条件)可以更好地保护我们,防止 FastCGI 尝试将其他文件解释为 PHP。
最后location
部分应如下所示:
location ~ [^/]\.php(/|$) {
fastcgi_split_path_info ^((?U).+\.php)(.*)$;
try_files $fastcgi_script_name =404;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
fastcgi_pass unix:/var/run/php5-fpm.sock;
fastcgi_index index.php;
}
其他想法
有一件事没人谈论,即使他们的做法各不相同。我说的是location
andfastcgi_split_path_info
指令中使用的正则表达式。就我个人而言,我选择了这一个,因为它们对我来说更有意义:
location ~ ^.+\.php(/|$) { ... }
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
我非常想知道另一个的优点和缺点。再次,很高兴能听到 Nginx 专家对此主题的见解。
来源
答案2
不确定为什么以下链接没有出现在这个对话中:try_files & $fastcgi_path_info
try_files 指令将请求的 URI 更改为文件系统上匹配的 URI,随后尝试将 URI 拆分为 $fastcgi_script_name 和 $fastcgi_path_info 会导致路径信息为空 - 因为 try_files 之后的 URI 中没有路径信息。
我认为这不应该被视为错误,而应该被视为 try_files 工作方式的一个特性。它使 try_files 与 fastcgi_split_path_info 一起使用不太方便,但有多种方法可以解决这个问题,包括上面提供的方法。
提及多于解决方法是您在问题中提到的那个。
因此,您发现的缺陷之一nginx
,并且(截至 2016 年)不会得到修复。您的解决方法完全有效,实际上,在随 Debian 一起分发的nginx
代码片段中引用了它。