补充:处理非 SNI 旧设备

补充:处理非 SNI 旧设备

我有一个包含 2 个域名的测试站点,我想为两个域名都启用 SSL。

此外,如果客户端访问这两个域之外的某个页面,我希望有一个默认服务器来显示错误页面。

这是我的nginx.conf

server {
    listen 443 ssl;
    server_name a.com;
    ssl_certificate a.cert;
    ssl_certificate_key a.key;
}
server {
    listen 443 ssl;
    server_name b.com;
    ssl_certificate b.cert;
    ssl_certificate_key b.key;
}
server {
    listen 443 default_server ssl;
    ...
}

问题:默认服务器必须具有有效的证书/密钥对,即使客户端正在连接a.comb.com

如果我未在默认 SSL 服务器上指定证书/密钥对,nginx 会给出此错误:

SSL 握手期间监听 SSL 端口的服务器中未定义“ssl_certificate”

我不明白这里的逻辑。启用 SNI 后,当客户端明确表示正在访问a.com,而 中有一个独立服务器块时nginx.conf,为什么 nginx 仍然要求默认服务器具有证书/密钥对?为什么它甚至要使用默认服务器?

答案1

您还可以使用ssl_reject_handshake用于 443 上的默认服务器。这样,您就不需要证书,并且最终用户的计算机上也不会出现“不受信任的站点”警告。

答案2

@MrA1Sauce 的想法很正确

ssl_reject_handshake on;

问题如下...如果您在 nginx 中执行虚拟托管(同一 IP 地址上具有不同 SSL 证书的多个域名),则 nginx 依赖服务器名称指示(“SNI”)TLS 扩展来获取有关将请求哪个主机名的提示,以便它可以在 HTTP 请求发生之前选择在建立 TLS 会话时要使用的适当的“虚拟主机”服务器块(和相应的 SSL 证书)。

对于不支持 SNI 的 Web 客户端,HTTPS 不适用于虚拟主机(一个 IP,多个证书),因为如果没有 SNI,Web 服务器就不知道要使用多个 SSL 证书中的哪一个进行响应。幸运的是,只有 Windows XP、旧版 Java 和旧版 Android 是不支持 SNI 的唯一“近期”客户端。它们的市场份额很小,可以忽略不计,除非您有一个非常特殊的用例,即拥有遗留客户群(在这种情况下,您可以考虑使用单个 SAN 证书来覆盖多个名称)。

即使使用 SNI,人们也可能使用不同的名称(任何人都可以将他们的域名指向您的 IP)或直接通过服务器的 IP 地址访问您的 nginx 服务器。在这些情况下,没有匹配的服务器块,那么 nginx 应该返回哪个证书?

server_name当入站请求没有匹配项时,nginx 默认server_name使用可以处理当前请求(端口号)的第一个字母。如果您指定 default_server,nginx 将使用该服务器,而不是假设默认服务器处理该端口(例如,对于 SSL 情况,HTTPS/443)。因此,如果您不希望非默认虚拟服务器之一处理未知请求...您的默认服务器必须支持 HTTPS/443。

通常这需要指定 SSL 证书/密钥...但是对于可能从随机名称或直接从 IP 地址获取请求的默认服务器,您应该使用什么证书/密钥?

使用ssl_reject_handshake on;在默认服务器中导致 nginx 仅仅“挂断”这些请求(未知的服务器名称)并且消除了您在默认服务器中指定某些证书/密钥的要求。

我们过去采用的一种技术是*在默认服务器上拥有一个长期有效的自签名通配符 () 证书,这样我们就有机会显示错误页面(如果用户绕过警告继续访问网站)。但是大多数浏览器都会阻止这种做法,不应鼓励最终用户安装/信任您的自签名通配符证书(安全问题)。此外,如果您有任何安全审计/扫描程序,它们可能会将此自签名证书标记为问题。

但我们试图完成的主要任务是避免在未找到其他匹配项时,将我们的某个真实虚拟主机/域作为未知名称的默认主机/域。正如我之前所说...如果您未指定处理 https 的 default_server,就会发生这种情况。

在默认的 https/443 服务器块中设置ssl_reject_handshake on;允许我们完全避免在 default_server 块中指定证书/密钥,并确保用户不会因证书错误而获得错误的网页,而只会在他们尝试通过 IP 或未知名称连接到网络服务器时收到 TLS 握手拒绝错误。

server {
    listen 443 ssl default_server;
    listen [::]:443 ssl default_server;
    ssl_reject_handshake on;
    
    # breaks https for non-SNI clients
}

补充:处理非 SNI 旧设备

如果您想在同一 IP 上托管多个名称并支持非 SNI 客户端...请考虑获取 SAN(“主题备用名称”)证书。这允许为该 SAN 证书涵盖的所有名称使用单个证书/密钥(单个服务器块)。基本上是一个多域证书。因此,无论客户端设备是否支持 SNI,nginx 都不必决定返回哪个证书,并且始终可以返回 SAN 证书。

LetsEncrypt 确实支持 SAN 证书,因此一旦您设置好它,维护起来就不会那么困难。

答案3

我有一个包含 2 个域名的测试网站。我想为这两个域名启用 SSL,并让默认 SSL 服务器处理错误(当客户端指定错误的域名时显示错误页面):

[...]

但我不明白这里的逻辑。如果客户端已经明确指定了它想要哪个域,为什么 nginx 还要费心使用默认服务器?

该证书对于 SSL 服务器是必需的,如果没有证书,那还有什么意义呢?HTTPS 就行不通了。

您必须在服务器块内部或外部定义 SSL 证书和密钥。如果您定义“默认服务器“,但您没有定义域,那么它将始终显示域与证书不匹配的警告(除非您使用通配符证书或其他一些特定用例)。

当你定义基于名称的 HTTPS 服务器,你必须牢记官方文档中的以下内容:

使用此配置,浏览器将接收默认服务器的证书,即 www.example.com,而不管请求的服务器名称是什么。这是由 SSL 协议行为引起的。SSL 连接是在浏览器发送 HTTP 请求之前建立的,并且 nginx 不知道所请求服务器的名称。因此,它可能只提供默认服务器的证书。

正如您所发现的,自签名证书可以实现这一点,并允许您在 default_server 中显示网页或重定向,以防没有匹配任何其他已定义的服务器名称块,尽管它(可能取决于配置)会出现警告。

如果你的问题是为什么你需要定义一个默认服务器,则不需要。如果您不指定 default_server 块,它将使用按字母顺序排列的第一个,如果有人使用未知域名访问您的服务器(通常不会发生),或者显示相应的服务器名称

更新

扩展我的回答以回应您的评论:

引用的部分就是我所问的。为什么 nginx 会在启用 SNI 的情况下提供默认服务器的证书(请继续阅读该页面服务器名称指示)

查看我上面的最后一段,这就是虚拟主机(或 nginx 术语中的服务器块)和默认服务器工作。Nginx 将服务于默认服务器如果您在中定义一个块服务器块服务器名称与其他块中的请求不匹配。您可以定义一个服务器块默认服务器 强行一网打尽到该特定块,否则,nginx 将默认使用第一个服务器块(在 nginx.conf 或 sites-enabled/conf.d 中(如果包含)。您可以定义它,否则 nginx 将为此选择第一个块,但服务器块将是默认块。

它不会发挥作用除非有人通过未定义服务器名称按照你的例子,假设有人将“c.com”指向你的 nginx 的 IP,那么 default_server 将捕获该请求并显示你定义的内容。

相关内容