专栏/防止SSL证书泄漏:Nginx 网络安全配置

防止SSL证书泄漏:Nginx 网络安全配置

2025年03月18日 01:26--浏览 · --点赞 · --评论
粉丝:86文章:2
(非) 日常篇:

 互联网上有很多神奇的东西,比如:明明某个网站使用 Nginx 搭建,并且已经使用了 高防 CDN,但是还是经常被 DDOS 打趴下,后来发现居然是源站泄露了,导致被攻击者绕过 CDN 捉住了源站

或者我在家中出口配置了一个 Nginx 用来反代内网的服务,并且部署了证书使用了 https,但是居然被运营商发现了绑定的域名,从而导致被怀疑搭建公开 HTTP 服务,签了保证书。

在这第一个案例中,明明攻击者只能知道网站的域名,由于使用了 CDN,背后源站的 IP 应该被完美隐藏了,为什么攻击者还能发现源站 IP 并精准攻击呢?

在第二个案例中,明明我没有分享自己搭建的服务,绑定了域名只是为了在特殊情况下在公网管理自己的内网服务,运营商最多也只能知道我的公网 IP,我的域名怎么会被发现呢?


搜查篇:

 这两个案例其实都和 SSL 证书泄漏有关,在没有正确配置 Nginx 的情况下,在直接访问 https://IP:443,并没有告知自己要访问的域名的情况下, Nginx 会直接尝试使用某一个已经配置了 SSL 的站点证书尝试连接,而源站的域名,则直接就写在 SSL 证书中。就这样,访问者轻松的获取到了该 Nginx 服务上配置的站点域名。

具体测试:

root@debian:~# curl -v -k https://xxx.xxx.xxx.xxx
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
* ALPN, server accepted to use http/1.1
* Server certificate:
*  subject: CN=www.baidu.com
*  start date: Nov 28 09:27:23 2023 GMT
*  expire date: Feb 26 09:27:22 2024 GMT
*  issuer: C=US; O=Let's Encrypt; CN=R3
*  SSL certificate verify ok.
> GET / HTTP/1.1
> Host: xxx.xxx.xxx.xxx
> User-Agent: curl/7.64.0
> Accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
* old SSL session ID is stale, removing
< HTTP/1.1 200 OK
< Date: Tue, 30 Jan 2024 18:24:33 GMT
< Server: Apache/2.4.38 (Debian)
< Vary: Accept-Encoding
< Transfer-Encoding: chunked
< Content-Type: text/html; charset=UTF-8

尝试使用 curl 的 -v -k 参数访问某个 IP 的 https 端口,服务器证书 (Server certificate) 直接就被返回了过来,而域名 www.baidu.com 就藏在其中。

学院裁判篇:
根据这个原理,攻击者就可以扫描全世界所有的ipv4地址(毕竟V4地址也仅有40亿个),如果服务器返回了该网站对应的域名,则记录下来,通过筛选,很容易就获取到了网站的源站IP。
注:如果攻击者针对你的网站域名进行 sni 扫描,源站依旧会被扫出来,如果想阻止这种情况,请使用防火墙只为 CDN 回源 IP 段添加白名单

而运营商同理,通过相同的操作,即可轻松获取到搭建在家里的HTTP服务的域名,尝试通过域名访问成功后就可以理所应当的怀疑搭建公共服务,下达通知书了。

而避免这样泄露源站IP的访问也非常简单,在Nginx 1.19.4版本中,添加了一个新的指令:ssl_reject_handshake on。当该指令被启用后,Nginx 将拒绝所有不带有效客户端证书的 SSL 握手。

与此同时配置 Nginx 拒绝掉所有尝试直接访问 IP 的用户,仅允许通过域名访问即可。

新增 Nginx 配置文件,设置为默认服务器,同时允许所有域名,这样设置是为了所有直接访问IP的用户会被该配置文件影响,同时返回 444 状态码,直接切断该连接:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    listen [::]:80 default_server ;
    listen [::]:443 ssl default_server ;
    server_name _; # 默认接受所有域名
    ssl_reject_handshake on;
    return 444; # 直接切断连接
}

再次尝试使用 curl 直接访问 IP,服务器会拒绝提供证书:

root@debian:~# curl -v -k https://xxx.xxx.xxx.xxx
*   Trying xxx.xxx.xxx.xxx...
* TCP_NODELAY set
* Connected to xxx.xxx.xxx.xxx (xxx.xxx.xxx.xxx) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: none
  CApath: /etc/ssl/certs
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS alert, unrecognized name (624):
* error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name
* Closing connection 0
curl: (35) error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name

而如果尝试直接在浏览器中访问 IP 呢?


投诉或建议