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;
}