一个网站的程序支持多站点模式,于是为了方便,在 Nginx 中绑定了域名 *.example.com
,但奇怪的是,访问记录中总是有一些奇怪的二级域名,让人隐约觉得不妥。本文记述使用 Nginx 的来规避类似的风险。
Wildcard 域名绑定
原本的 Nginx 大约如下:
server {
listen 80;
server_name *.example.com;
// blabla
}
这样配置简单方便。不过如果你的网站并不是多站点模式,或者网站程序未能恰当地处理,会导致任意二级域名却都能正常访问网站内容,是有点奇怪的。也许有人故意构造并发布了一个链接,不幸又被爬虫抓取,想想一下,从这个流量来源过来的访客们,纷纷皱着眉头看着你的奇怪的二级域名。这是一个未预期的行为,会影响 SEO,且有一定的合规风险,应当采取一些措施。
如果二级站点数量有限,可以手动列出全部二级域名:
server {
listen 80;
- server_name *.example.com;
+ server_name
+ www.example.com
+ cdn.example.com
+ api.example.com;
}
倘若你有更复杂的需要,仍需要保留 *.example.com
以简化配置,那么就可以加一个白名单的校验。
$server_name
代表的是 server_name 配置中的第一个值,因此将网站的主域名放在第一位就非常方便后续处理。如果你曾经使用过 blog.example.com,如今回归到 www.example.com, 那么更合适的办法应该是重定向。而其他未预期的域名都可以使用 return 444;
来直接断开连接。
server {
listen 80;
- server_name *.example.com;
+ server_name www.example.com example.com *.example.com;
+
+ if ($host ~* ^((blog|feed|log)\.example\.com|example.com)$) {
+ return 307 $scheme://$server_name$request_uri;
+ }
+
+ if ($host !~* ^(cdn|api)\.example\.com$) {
+ return 444;
+ }
}
默认主机
除了泛域名绑定之外,默认主机也会有类似的问题。如果在中国大陆,有未备案的域名指向到你的服务器,也有监管的风险。直接让默认主机断开所有连接:
server {
listen 80 default_server;
server_name _;
return 444;
}
在某些情况下,你可能需要在阻断未指定域名的同时,支持使用 IP 地址直接访问:
-return 444;
+if ($host !~* "^((?:\d{1,3}\.){3}\d{1,3}|(?:[a-fA-F0-9:]+))$") {
+ return 444;
+}
SNI 域名泄露
在尝试 HTTPS 访问时,即便 Nginx 断开了连接,但客户端仍能够从返回的证书中获取 SNI 域名列表。很多人使用 CDN 全站加速来隐藏真实 IP,被扫描 IP 时,可能就会泄漏服务器与域名的关联。
可以在 server 块中配置,拒绝 ssl 握手:
+ssl_reject_handshake on;
如果你还是希望能够支持使用 IP 地址直接访问……那么,要使用一个 IP 专用的 SSL 证书。IP 证书购买渠道有限,自签一个也是可以的。
生成一个 IPv4 地址的证书:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ip.key -out ip.crt \
-subj "/CN=1.2.3.4" \
-addext "subjectAltName=IP:1.2.3.4"
如果你还希望支持 IPv6……建立自签证书的配置 openssl-san.conf
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
CN = (ip or domain or keep bank)
[v3_req]
subjectAltName = @alt_names
[alt_names]
IP.1 = 1.2.3.4
IP.2 = 2001:db8::1
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout ip-mixed.key -out ip-mixed.crt \
-config openssl-san.conf
然后配置 Nginx:
server {
listen 80 default_server;
listen [::]:80 default_server;
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name _;
ssl_certificate /path/to/ip.crt;
ssl_certificate_key /path/to/ip.key;
if ($host !~* "^((?:\d{1,3}\.){3}\d{1,3}|(?:[a-fA-F0-9:]+))$") {
return 444;
}
}
避免 CDN 域名被收录
root /path/to/webroot/www.example.com;
location = /robots.txt {
default_type text/plain;
if ($host = 'cdn.example.com') {
#return 200 "User-agent: *\nAllow: /*.png$\nDisallow: /\n";
alias /path/to/webroot/www.example.com/robots-cdn.txt;
}
}
Webmention
评论表单