nginx:如何防止完全命名的SSL服务器块充当所有SSL的包罗万象


17

我有一个包含许多虚拟服务器的Web服务器。其中只有1个是SSL。问题是,因为没有侦听SSL的全部服务器块,所以对其他站点的任何https请求都由1个SSL块提供。

实际上,我的配置如下所示:

# the catch all
server {
  listen 80 default;

  # I could add this, but since I have no default cert, I cannot enable SSL,
  # and this listen ends up doing nothing (apparently).
  # listen 443; 

  server_name _;
  # ...
}

# some server
server {
  listen 80;
  server_name server1.com;
  # ...
}

# some other server ...
server {
  listen 80;
  server_name server2.com;
  # ...
}

# ... and it's https equivalent
server {
  listen 443;
  ssl on;
  server_name server2.com;
  # ...
}

现在,由于没有默认的443侦听器,像这样的请求https://server1.com最终将由server2.comhttps块提供服务。这遵循的逻辑server_name的文档。

如果不匹配,将根据以下顺序使用配置文件中的服务器{...}块:

  1. 具有匹配的侦听指令的服务器块,标记为[default | default_server]
  2. 具有匹配侦听指令的第一个服务器块(或隐式侦听80;)

此问题的首选解决方案是什么?我是否需要为捕获所有服务器块设置虚拟证书,以便我可以侦听443并处理错误的请求?是否有我不知道的参数会强制与之精确匹配的主机名server


当人们尝试使用https访问其他站点时,您想发生什么?
大卫·史瓦兹

理想情况下,除非主机名匹配,否则我要么希望nginx根本不提供https,要么将其重定向到同一主机上的http。
number1311407 2011年

Answers:


9

理想情况下,除非主机名匹配,否则我要么希望nginx根本不提供https,要么让它重定向到同一主机上的http。

都不可能。来自客户端的连接到https://foo.example.com/的连接只能由以“ foo.example.com”作为其名称之一的SSL证书接受。在接受SSL连接之前,没有机会进行重定向。

如果为每个站点配置SSL,则单击证书错误的用户将获得他们请求的站点。如果为SSL配置一个“全部捕获”站点,该站点仅提供一个错误页面,并为应该支持SSL的一个站点配置基于名称的虚拟主机,则可以为客户端提供一个错误页面。

SSL和HTTP虚拟主机不能很好地配合使用。


这是我阅读文档后收集的。我只是希望我会错过一些东西。我根本不在乎SSL警告。我只是不想让某人进入server1.com并发现自己正在查看server2.com的主页...真的没有办法告诉nginx 接受请求吗?
号码1311407 2011年

如果它不接受请求,则第一个站点将无法工作。它必须接受请求以找出用户尝试访问的站点。
David Schwartz

2
“来自客户端的连接到foo.example.com的连接只能被SSL证书(以“ foo.example.com”作为其名称之一)接受。-这是不正确的,服务器将接受请求,并且由客户端来验证所请求的DN是否与服务器证书DN匹配。
ColinM '18年

4

唯一的方法是创建一个自签名SSL证书,并使用它来控制传入的https请求。您可以通过本文概述的几个简单步骤创建自签名SSL证书。

假设您使用文件名server.crt创建了一个自签名证书。然后,将以下内容添加到您的nginx配置中:

server {
    listen  443;

    ssl    on;
    ssl_certificate         /etc/nginx/ssl/server.crt;
    ssl_certificate_key     /etc/nginx/ssl/server.key;

    server_name server1.com;

    keepalive_timeout 60;
    rewrite ^       http://$server_name$request_uri? permanent;
}

您仍然会收到浏览器SSL警告消息,但是至少您可以控制接下来发生的事情。


1
我同意这一点。有一个问题,浏览器会显示有关不可信证书的警告消息,但是如果您只是想阻止用户访问https:// <ip-address>来获取服务器真实有效虚拟机之一的同样无效的证书(无效(因为主机名不匹配),那么最好为它们提供无效的自签名虚拟证书。这样告诉他们“这里什么也看不见,甚至没有其他主机的证书”。
丹尼尔·F

2

添加一个包罗万象的服务器块并返回状态代码444。它告诉nginx在发送任何数据之前关闭连接。

server {
    listen 443 default_server ssl;
    server_name _;
    return 444;
}

1

这些天,您可以使用TLS服务器名称指示扩展名(SNI,RFC 6066)。在提供适当的证书之前,HTTPS侦听器将能够识别域名。

这意味着您将需要拥有所有域的证书,并且当使用SNI识别其他域之一时,除非服务器名称与需要的单个域名匹配,否则您只能使用HTTP 301重定向到HTTP非加密版本。加密。

有关SNI的更多信息,请参见Nginx文档http://nginx.org/en/docs/http/configuring_https_servers.html


0

将请求的主机名映射到http {}块中的有效主机名:

map $ssl_server_name $correct_hostname_example {
  default 0;
  example.com 1;
  www.example.com 1;
}

然后在server {}块中终止使用错误主机名的连接:

if ($correct_hostname_example = 0) {
  return 444;
}

对于多个服务器块,根据需要使用多个映射。仍将使用您的证书之一建立连接,但是如果在提供SSL的每个服务器块中都存在该最后一个块,则您将有效地“阻止”具有无效主机名的连接。可能仅在第一个服务器块中才有必要,但是将其添加到每个服务器块将确保顺序无关紧要。

$ssl_server_name变量存在于nginx 1.7或更高版本中。


0

这就是我解决问题的方法:

  1. 创建自签名证书:

openssl req -nodes -x509 -newkey rsa:4096 -keyout self_key.pem -out self_cert.pem -days 3650

  1. 将其复制到NginX可以找到的位置:

cp self*.pem /etc/nginx/ssl/

  1. 设置全部路线:
server {
    listen 443 default_server ssl;

    ssl on;
    ssl_certificate /etc/nginx/ssl/self_cert.pem;
    ssl_certificate_key /etc/nginx/ssl/self_key.pem;

    return 301 http://$host;
}

它将执行的操作:在没有自己的证书的任何服务器上,它将向您发出警告(无法解决),但是警告不会说出错误的证书名称。如果用户单击“仍然访问”,他们将被重定向到他们输入的网站的非SSL版本。

警告

如果您的启用SLL的网站仅定义www.example.com(而不是example.com),那么您的全部路由将最终https://example.com带有自签名证书和相应的警告。


-2

重定向到http:

server {
    listen       443;
    server_name  *.com;
    rewrite        ^ http://$host$request_uri? permanent;
}    

返回404:

server {
    listen       443;
    server_name  *.com;
    return 404;
}    

1
这仍然会导致SSL警告,因为需要在任何HTTP重定向发生之前建立SSL隧道。请参阅David Schwartz接受的答案。
cjc'2
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.