配置 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 版本,将来可以用上。或者也可以考虑第三方源。
已使用最后一个方法,感谢分享
亲测第二个方法有用 谢谢