即使 HTTP HOST 标头与 SNI 不同,Nginx 也会响应 200

即使 HTTP HOST 标头与 SNI 不同,Nginx 也会响应 200

我刚刚开始学习 nginx 和 SSL,并且一直在尝试不同的配置。我有两个服务器块,如下所示:

server{
    listen 443 ssl http2;
    server_name www.a.com;
    ssl_certificate a.crt;
    ssl_certificate_key a.key;
    ....
}
server{
    listen 443 ssl ;
    server_name www.b.com;
    ssl_certificate b.crt;
    ssl_certificate_key b.key;
    ....
}

使用 www.a.com 作为我的 SNI 通过 openssl s_client 连接到 nginx 后,我使用 Host: www.b.com 发送了一个 GET 请求,它仍然有效。这是预期的吗?有人能帮我理解 nginx 的行为吗?

答案1

从根本上讲,HTTP 连接只是 TCP 连接。因此,上述内容实际上适用于 HTTP 连接(即,如果您连接到www.a.com,你实际上连接的是 IP 地址,而不是www.a.com,因此该连接能够处理以下请求是正常的,也是意料之中的www.b.com)。

对于 HTTPS,就像您说的您正在使用的,它不太明确......

对于 HTTP/1.1,HTTP Over TLS RFC也不是超文本传输​​协议 (HTTP/1.1):消息语法和路由 RFC,说明应如何处理,因为 SNI 是之后添加的 HTTP 的非强制性扩展(事实上,顺便说一句,这是绕过客户端的一种方法 - 比如 XP 上的 IE8 - 如果两个虚拟主机使用相同的证书,那么在基于默认主机提供的证书协商 TLS 会话后,可以使用适当的连接)。然而SNI RFC 明确指出,服务器必须检查这是否相同,因此当给出的服务器名称与主机不同时,您观察到的内容是不正确的(感谢 Michael Hampton 指出这一点,因为我最初错过了这一点)。

HTTP/2 在这方面更加明确,事实上确实允许在特定条件下重用连接,并且有话要说

只要源服务器具有权威性,连接就可以重用(第 10.1 节)。对于没有 TLS 的 TCP 连接,这取决于主机是否解析为相同的 IP 地址。

对于 https 资源,连接重用还取决于是否拥有对 URI 中的主机有效的证书。服务器提供的证书必须满足客户端在为 URI 中的主机建立新的 TLS 连接时执行的所有检查。

源服务器可能会提供具有多个 subjectAltName 属性或带通配符的名称的证书,其中一个对 URI 中的颁发机构有效。例如,subjectAltName 为 *.example.com 的证书可能允许使用同一连接来请求以 开头的 URIhttps://a.example.com/https://b.example.com/

因此如果 a.crt 还包括 includewww.b.com上面的“主题备用名称”字段在 HTTP/2 下实际上是可以的,但在 HTTP/1.1 下则不行。

但是,为了确保万无一失,我重复了你的观察,因为 a.crt 肯定不涵盖www.b.com这听起来确实像是一个漏洞,也可能存在安全风险,因为如果有人能够注入无效的主机头(幸好浏览器不容易做到这一点 - 所以老实说我无法证明这一点),那么对一个虚拟主机的请求就可以被另一个虚拟主机读取(尽管无法访问解密该消息的密钥)。这可能会将 cookie 或其他信息泄露给辅助服务器。

我已经向 nginx 安全团队提出了这个问题,他们说这不是 nginx 的安全漏洞,客户端应该验证证书,然后不要发送格式错误的请求,而这些请求是他们应该通过该连接访问的主机。我强烈反对这种说法,并说 HTTPS 的重点是只允许有效的端点解密消息!所以我认为检查应该在客户端和客户端上进行服务器端,事实上TLS 工作组当时讨论过这种确切的情况,认为 SNI 是特定的,这就是为什么上述措辞被添加到 RFC 中。此外,nginx 安全团队表示,在这种情况下,nginx 应该为两个主机设置联合证书(这对我来说毫无意义,因为严重限制了 nginx 中虚拟主机的实用性!),但如果 nginx 服务器操作员真的想限制这一点,那么他们可以通过检查变量来实现$ssl_server_name(对我来说,这意味着 nginx 默认可以进行相对简单的检查!)。无论如何,这是他们的软件,所以他们有权发表自己的意见,但我不同意。

还有人指出,这种技术有时被用来逃避审查,这个过程被称为域名前端。这很公平,但我认为应该明确启用,而不是默认允许。

顺便说一句,Apache 不会做同样的事情,并且(在我看来这是正确的)在 2.4.17 之前返回“400 错误请求”,在 2.4.17 之后返回新的“421(错误请求)” -这是专门为这种情况创建的新的 HTTP 状态代码

相关内容