Nginx 设置严格域名校验

文章目录

配置 HTTP/HTTPS 网站时,Nginx 默认不严格校验 Host/SNI 字段。也就是说,假如我们只配置了 server_name example.com www.example.com,但用户使用 IP 地址访问,仍然会访问到这个网站(server);如果有一个别的什么域名解析到这个 IP 地址,也是一样。

这有时候会造成安全问题。互联网时时刻刻有机器人在扫描,使用 http://https:// 加上各个 IP 地址访问,记录下使用的 IP 地址以及响应结果。我们可能使用 CDN 隐藏真实 IP 地址,但假若不限制 IP 直接访问,相应网站的真实 IP 地址仍然会被记录下来进而泄露。在另一些情况下,传闻会造成误判滥用导致政策性封禁,不过这应多发于 HTTP 网站。

配置默认 server

要解决问题很简单,我们用 default_server 设置默认服务器就行了。对于 HTTP 协议,可以这么写:

server {
    listen 80 default_server;
    return 444;
}

其中 444 是 Nginx 自定义的、非 HTTP 标准的状态码,表示关闭连接;写成 403 之类也没问题。server_name 省略不写。

HTTPS 类似,但需要 SSL 证书。可以用现成的证书,但其中会有域名等信息。如果介意,自签名一张就好。

要自签名证书,我们先生成私钥(选择其一):

openssl genrsa -out /opt/self-signed.key 2048 # 2048 位 RSA
openssl ecparam -name secp384r1 -genkey -out /opt/self-signed.key # 384 位 ECC

然后根据私钥生成证书:

openssl req -new -x509 -days 3650 -key /opt/self-signed.key -out /opt/self-signed.crt -subj "/C=CN/ST=NULL/L=NULL/O=NULL/OU=NULL/CN=Please-enable-SNI"

其中 3650 是有效期天数,这里是 10 年。Please-enable-SNI 就是通用名称(Common Name),一般这里就是主域名,但我们自签名可以随便写,反正不受信任,也不需要被浏览器信任。

之后配置 HTTPS 默认服务器:

server {
    listen 443 ssl default_server;
    ssl_certificate /opt/self-signed.crt;
    ssl_certificate_key /opt/self-signed.key;
    return 444;
}

记得将相应路径依实际情况替换。

小结一下,将 HTTP、HTTPS 配置写在一起就是:

server {
    listen 80 default_server;
    return 444;
}
server {
    listen 443 ssl default_server;
    ssl_certificate /opt/self-signed.crt;
    ssl_certificate_key /opt/self-signed.key;
    return 444;
}

噢,谁说我不可以合并呢:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    ssl_certificate /opt/self-signed.crt;
    ssl_certificate_key /opt/self-signed.key;
    return 444;
}

SNI 校验参数:ssl_reject_handshake

为了配置默认 HTTPS 服务器,我们得模拟一个网站出来,得自己准备一张证书,似乎有点麻烦。好在自 1.19.4 起,Nginx 支持 ssl_reject_handshake 参数,设置为 on,当客户端传过来的 SNI 与已配置的 server name 都不匹配时,会拒绝 SSL 握手。以往有第三方补丁支持类似功能,这次是官方支持。

server {
    listen 443 ssl default_server;
    ssl_reject_handshake on;
}

这样写就足够阻止上述恶意 HTTPS 访问了;访问时客户端(浏览器)会报 SSL_ERROR_UNRECOGNIZED_NAME_ALERT。但是注意这对 HTTP 没有用,所以你可能想完整配置为:

server {
    listen 80 default_server;
    return 444;
}
server {
    listen 443 ssl default_server;
    ssl_reject_handshake on;
}

等价合并写法:

server {
    listen 80 default_server;
    listen 443 ssl default_server;
    ssl_reject_handshake on;
    return 444;
}

主流 Linux 发行版默认源可能还没有跟进这么新的 Nginx 版本,将来可以用上。或者也可以考虑第三方源。

全部为采集文章,文中的 联系方式 均不是 本人 的!

发表评论