Nginx设置严格域名校验

配置 HTTP/HTTPS 网站时,Nginx 默认不严格校验 Host/SNI 字段。也就是说,假如我们只配置了 server_name 2dan.cc www.2dan.cc,但用户使用 IP 地址访问,仍然会访问到这个网站(server);另外使用 https://IP 访问,再查看SSL证书,会泄露IP上绑定的域名。

解决:

一、配置默认 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;
    listen 443 ssl default_server;
    ssl_reject_handshake on;
    return 444;
}

标签:Nginx, 域名, 证书, 配置, https, http, 校验, 设置, 默认

添加新评论