当主机头包含冒号时,NGINX 路由到错误的虚拟主机

当主机头包含冒号时,NGINX 路由到错误的虚拟主机

我的 nginx.conf 有几个“服务器”部分和一个包罗万象的服务器部分。下面是一个示例 nginx.conf,供您参考:

user www-data;
worker_processes auto;
worker_cpu_affinity auto;
pid /run/nginx.pid;

events {
    worker_connections 4000;
    use epoll;
    accept_mutex off;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    error_log /var/log/nginx/error.log;

    server {
        listen 80;

        server_name foo.com;

        location / {
            default_type text/plain;
            return 200 "hello from foo.com";
        }

        error_page 500 502 503 504 /500.html;
    }

    server {
        listen 80 default_server;
        server_name _;

        location / {
            return 403 "sorry";
        }
    }

}

如果“Host”标头不是“foo.com”,我期望服务器返回 403。

有人显然在我的服务器上运行 Burp Suite,当他们发送“Host: foo.com:more-stuff-here”标头时,我注意到一个有趣的行为:NGINX 将请求路由到第一个“服务器”部分。它似乎忽略了标头值中的冒号及其后的所有内容。

我可以使用上述 nginx.conf 在本地重现它:

$ curl -H "Host: foo.com" http://127.0.0.1
hello from foo.com

$ curl -H "Host: foo.com:unexpected-content" http://127.0.0.1
hello from foo.com

$ curl -H "Host: bar.com" http://127.0.0.1
sorry

NGINX 为什么会这样做?这是预期的行为吗?我应该在 nginx.conf 中更改哪些内容以确保带有“Host: foo.com:more-stuff-here”标头的请求转到默认块?

更新:对于任何研究同一问题的人,我还创建了NGINX 问题跟踪器中的票据

答案1

主机头的定义HTTP 请求表示 Host 头应该具有 形式host:port,其中:port是可选的。

nginx 将冒号后面的所有内容视为主机的端口,但由于您没有以这种方式指定服务器块,因此它与您的上下文无关。因此,它会使用能找到的最接近的匹配项,即不带“端口”的主机。

答案2

以下可能适用于default_server

server {
    listen 80 default_server;

    server_name _ ~example\.com:;

    location / {
        return 403 "sorry";
    }
}

重要的部分是波浪号~,它表示正则表达式匹配。

答案3

我能想到的唯一替代想法是在服务器端应用程序级别编写一些代码来检查主机值并以代码形式返回 403 响应。

然而,这并不是真正正确的做法,因为这个问题是客户端的问题,而不是您的应用程序的问题。

例如,取自这个答案给定一个 PHP 应用程序,您可以轻松修改此逻辑以实现所需的效果,特别注意检查 SERVER_PORT 是否符合杰拉尔德的回答

$allowed_hosts = array('foo.example.com', 'bar.example.com');
$allowed_ports = array(80,443);
if (!isset($_SERVER['HTTP_HOST']) || !in_array($_SERVER['HTTP_HOST'], $allowed_hosts) || !in_array($_SERVER['SERVER_PORT'], $allowed_ports )) {
    header($_SERVER['SERVER_PROTOCOL'].' 400 Bad Request');
    exit;
}

相关内容