Nginx - “if”可以在服务器块级别安全地使用吗?

Nginx - “if”可以在服务器块级别安全地使用吗?

Nginx 文档明确警告称if 是邪恶的并且应尽可能避免,互联网上到处都有类似的警告。

但是,大多数警告都特别关注块if中的不良行为location。此外,Nginx 文档说:

在位置上下文中可以完成的唯一 100% 安全的事情是:

  • 返回 ...;

  • 重写...最后;

我的问题是:如果其中包含的唯一指令是,那么if在块级别server(而不是块)使用是否安全?例如,以下 www-to-non-www 重定向:locationreturn

server {
    listen 80;
    server_name example.com www.example.com;

    if ($http_host != example.com) {
        return 301 http://example.com$request_uri;
    }

    # more config
}

第二个问题:我知道做这种事情的推荐方法是使用两个server具有不同值的块server_name。如果这个是可以接受的用途if,还有理由继续使用两个独立的server块吗?

答案1

nginx 收到请求时的第一步是评估 HTTPHost标头/TLS SNI 字段,并根据评估结果选择虚拟主机。

这意味着Host标题总是被处理。

现在,如果您指定if重定向语句,则意味着Host将检查两次标头,首先选择虚拟主机,然后检查条件if。这对处理器来说是两倍的工作。

一个反驳可能是单独服务器块的内存消耗。然而,内存分配在 nginx 进程的整个生命周期中都是相同的,而Host标头的双重评估发生在每个请求上。

答案2

摘自《如果是邪恶的》:

在某些情况下,也可以将 if 移动到服务器级别(这是安全的,因为其中只允许其他重写模块指令)。

这表明if在一个server块中比在一个块中更安全location

答案3

不确定你说的安全是什么意思,但让我讲一下我最近遇到的一个案例。我需要拒绝访问某个网站,除非传递了某个参数。假设配置如下所示:

server {
    server_name localhost;
    root /usr/share/nginx/html;
    try_files $uri /index.html;
}

我添加了一个if

    ...
    if ($arg_secret-key != 123456) {
        return 404;
    }

nginx即使我提供了秘密,也会给我 404:

$ curl -sS localhost/?secret-key=123456

事实证明,try_files进行了内部重定向/index.html(未传递参数),导致 404。因此,为了使其正常工作,需要这样做:

try_files $uri /index.html$is_args$args;

但实际上我只需要拒绝访问主页:

    ...
    location = / {
        if ($arg_secret-key != 123456) {
            return 404;
        }
    }

这样,它就可以正常工作而无需更改try_files。这是因为还有index模块。并且try_files不会被location块继承。因此,index模块会正确重定向到/index.html?secret-key=123456,并且对于新请求,无需对 进行任何操作try_files

如果您不确定发生了什么,您可能需要运行nginx内置程序--with-debug并使用级别配置错误日志debug,例如:

nginx.conf

error_log  /var/log/nginx/error.log debug;
server {
    server_name localhost;
    root /usr/share/nginx/html;
    ...
}
$ docker run --rm --name nginx -p 1111:80 -dv $PWD/nginx.conf:/etc/nginx/conf.d/default.conf nginx:alpine nginx-debug -g 'daemon off;'
// launch `docker logs -f nginx` in a separate console, use Enter to separate requests
// processing starts with "generic phase: 0"
$ curl -sS localhost:1111/?secret-key=123456
// change nginx.conf
$ docker exec nginx nginx -s reload
$ curl -sS localhost:1111/?secret-key=123456
...
$ docker stop nginx

更多内容这里

关于你的第二个问题,我认为在你的情况下server最好使用 2 。因为这是开发人员建议的,如果你选择该if版本,有一天你可能会在更改配置后遇到一些问题。或者你可能会意识到在某些情况下它不会像你预期的那样工作(在更复杂的情况下)。有人可能会说,if在你可能不知道的配置中引入了复杂性。if如果我对它的工作原理有很好的理解,或者没有其他选择,我会使用它。而且这个if版本看起来性能可能更差。虽然这只是一个猜测,我无法提供任何数字。就我而言,我没有看到任何解决方法,因此这就是我现在要使用的。

答案4

可以,但强烈不建议。已经有官方关于这个的解释

要运行多个站点,我建议为每个站点创建多个配置文件,然后在主文件中包括位置nginx.conf

相关内容