相信使用 Cloudflare 建站的同学,其目的都是为了隐藏服务器 IP,但是有些无良爬虫可通过 HTTP/HTTPS 访问扫描全网 IP 绕过 CDN,暴露证书、同时暴露你的域名。
就比如下面 Nginx 常规 HTTPS 跳转配置就会轻易暴露你的 IP:
server {listen 80;listen [::]:80;server_name example.com;location / {return 301 https://$server_name$request_uri;}}
如果你 Nginx 上有这种配置,运行下面命令就可以获取你的域名了(假设你的IP是 1.2.3.4)
curl -v -k http://1.2.3.4
或者直接浏览器输入 http://1.2.3.4 也会跳转到你的域名。解决办法是,直接删除上面 80 端口配置,只保留 443 端口配置。也就是将 HTTPS 作为唯一的回源协议。
Nginx 上开启的端口越少,暴露的风险也就越低,这是显而易见的,就比如 Cloudflare 建站的用户,只需要开启 443 端口就够了。这里又有人问了,443 端口也可以用类似命令扫描呀!对,下面一条命令不止暴露域名,连证书都可以暴露:
curl -v -k https://1.2.3.4
curl -v -k https://1.2.3.4* Rebuilt URL to: https://1.2.3.4/* Trying 1.2.3.4...* TCP_NODELAY set* Connected to 1.2.3.4 (1.2.3.4) port 443 (#0)* ALPN, offering h2* ALPN, offering http/1.1* successfully set certificate verify locations:* CAfile: /etc/ssl/certs/ca-certificates.crtCApath: /etc/ssl/certs* TLSv1.3 (OUT), TLS handshake, Client hello (1):* TLSv1.3 (IN), TLS handshake, Server hello (2):* TLSv1.2 (IN), TLS handshake, Certificate (11):* TLSv1.2 (IN), TLS handshake, Server key exchange (12):* TLSv1.2 (IN), TLS handshake, Server finished (14):* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):* TLSv1.2 (OUT), TLS change cipher, Client hello (1):* TLSv1.2 (OUT), TLS handshake, Finished (20):* TLSv1.2 (IN), TLS handshake, Finished (20):* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384* ALPN, server accepted to use http/1.1* Server certificate:* subject: CN=example.com* start date: Nov 15 05:41:39 2019 GMT* expire date: Nov 14 05:41:39 2020 GMT* issuer: CN=example.com* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.> GET / HTTP/1.1> Host: 1.2.3.4> User-Agent: curl/7.58.0> Accept: */*>* Empty reply from server* Connection #0 to host 1.2.3.4 left intactcurl: (52) Empty reply from server
开启 ssl_reject_handshake 插件
可以看见,证书和域名都暴露了。解决方法也很简单,nignx 开启 ssl_reject_handshake 插件就能防止 443 端口被扫,下面讲讲使用方法。
Nginx 版本高于等于 1.19.4,才可以使用 ssl_reject_handshake 特性来防止 SNI 信息泄露。如果 Nginx 版本太低,请升级版本。
Nginx 里面新添加一个 server 块,即可开启 ssl_reject_handshake,如下:
#新添加的443端口块,如果使用了错误的 Hostname,SSL 握手会被拒绝server {listen 443 ssl default_server;#如果有IPv6地址需加入下面这行,否则不用下面这行listen [::]:443 ssl default_server;ssl_reject_handshake on;}#常规的443端口,包含正确的域名和证书。对于携带正确 Hostname 的请求,服务器会继续做后续处理server {listen 443 ssl;listen [::]:443 ssl;server_name example.com;ssl_certificate example.com.crt;ssl_certificate_key example.com.key;}
注,上面插件只适用于 443 端口不适用其它端口。重启 Nginx 后,再运行上面命令,看看还会不会暴露域名。
IP 白名单
实际上还有更直接的方法,那就是配置 Cloudflare IP 白名单,服务端只允许 Cloudflare IP 的请求,其它丢弃掉。下面是利用 iptables 来设置 CF IP 白名单的方法。
有人问,为什么不用 Nginx 内置的 access module 来配置白名单?
因为使用 HTTPS 作为回源协议,爬虫依旧能通过探测证书 SNI 信息来找到你的源站服务器,所以才用 iptables 来配置白名单。
添加cloudflare ips-v4 iptables 白名单的命令:
for i in `curl https://www.cloudflare.com/ips-v4`;do iptables -I INPUT -p tcp -m multiport --dports http,https -s $i -j ACCEPT;done
添加cloudflare ips-v6 iptables 白名单的命令:
for i in `curl https://www.cloudflare.com/ips-v6`;do ip6tables -I INPUT -p tcp -m multiport --dports http,https -s $i -j ACCEPT;done
丢弃白名单以外的 ipv4 80,443 tcp 包:
iptables -A INPUT -p tcp -m multiport --dports http,https -j DROP
丢弃白名单以外的 ipv6 80,443 tcp 包:
ip6tables -A INPUT -p tcp -m multiport --dports http,https -j DROP
如果是用UFW,请参考这边文章UFW只允许CLOUDFLARE访问
第三种
其实应该还有第三种方法,俺来假设一下,现在爬虫的 HTTP 版本基本都是 1.1,指定 HTTP 版本号来屏蔽爬虫,理应能达到同样效果。
Nginx 里 server 模块中引入下面规则文件:
#限制http版本号,只允许下面3个HTTP版本if ($server_protocol !~* "HTTP/2.0|HTTP/3.0|SPDY/3.1") {return 403;}
还可以引入以下规则,来抵御 CC 攻击,下面规则比较暴力,非 Cloudflare 用户可以酌情引用。
#禁止非 Mozilla/ 请求头的访问if ($http_user_agent !~* "Mozilla/") {return 403;}
#禁止非GET|HEAD|POST方式的抓取if ($request_method !~ ^(GET|HEAD|POST)$) {return 403;}
#禁止Scrapy等爬虫工具的采集if ($http_user_agent ~* (Scrapy|Curl|HttpClient)) {return 403;}
结语
对于爬虫爬取你的真实 IP,上面三种方法,可以三选一。(第三种假设没经过测试,不确保可用性)
俺以前有一点误区,以为在 Cloudflare 上配置相应规则能屏蔽这些爬虫,其实这是不对的,因为它们压根不搭理你域名,只是扫全网IP,探测证书 SNI 信息,所以在 CF 上折腾相关规则完全是无用功。