最大程度屏蔽互联网对 IP 的扫描
前言
早在 2020 年我刚会玩 Linux 那会,就想写这个主题了,那时候是准备作为 Debian 系统安全指南来写一篇的,可惜不停的搁置。事到如今,这种大而全的文章我已无力再写,不过如本标题而言,针对性某一方面还是可以写写的。刚会建站就发现这个问题了,直接使用 HTTPS 协议访问源站 IP 的 443 端口,也就是 https://IP:443
会暴露你源站的 TLS 证书,这点不多赘述。解决方案随着软件版本迭代和需求的不同,也逐渐完善,下面依次介绍
签发空白假证书
2020 年上半年及之前,统一的解决方案是随便签个空白假证书,很简单的一行命令 openssl req -new -x509 -nodes -out fake.crt -keyout fake.key
,然后写到默认配置里,这样访问 https://IP:443
会得到这个空白假证书,达到保护源站的目的。示例配置如下
1 | # /etc/nginx/sites-enabled/default |
使用 nginx 新特性
而在 2020/10/27 发布的 nginx 1.19.4 带来了 ssl_reject_handshake
特性后,情况发生了变化。话说我能用上这个功能足足等了三年,Debian 12 发布才附带了 nginx 1.22(Debian 11 是1.18)。简单来说开启本功能后会拒绝握手(并且不会在 /var/log/nginx/access.log
留下日志),相比于上个方案的优点是简单方便通用性强,不需要自己签多余的假证书。并且还有额外的好处,通过 Wireshark 抓包可以发现,发送无效的握手信息后还会发送 Connection reset (RST),在一定程度上可以对访问者隐藏自己是 HTTP/HTTPS server 的事实(虽然没什么大用)。示例配置如下
1 | # /etc/nginx/sites-enabled/default |
状态码重定向
上面的配置仍有两个问题
- 访问
http://IP:443
会返回 400 Bad Request The plain HTTP request was sent to HTTPS port - 通过查看日志,有一些访问者使用非 HTTP 协议进行扫描,也会返回 400 Bad Request,并在日志留下记录,比如
curl scp://localhost:80
或curl scp://localhost:443
这倒不是什么大问题,只是在日志看到 400 响应而非 444 响应心里有点膈应。但在前几天刷到的帖子里的一个回复,经过我尝试并完善后,完美解决了这两个问题,参考
V2EX - Nginx 的 444 命令(强制切断 TCP 连接),用于对抗运营商检测家宽开服务是否有帮助?
十楼提到了 error_page 400 =444 /;
,但其实经我测试,单纯这样写没用,根据 Stack Overflow - Return 444 Instead of 400 上的回答补足后,示例配置如下
1 | # /etc/nginx/sites-enabled/default |
配置文件简化
上面的配置有太多重复了,于是我对其进行了简化,经测试效果是完全一样的(甚至可能更好)
于是这就是最终配置了,可以直接复制过去用,感兴趣的话也可以继续看下面的测试
1 | # /etc/nginx/sites-enabled/default |
测试用例
顺便把测试结果展示一下,这里还是挺费劲的,我是 tmux 上下开弓,又结合 Wireshark 抓包,使用了 curl、nc、nmap 进行了大量测试
基础
1 | curl http://localhost:80 # Return 444 |
进阶
1 | echo test | nc localhost 80 # Return 444 |
专业
这里为了排除已知常用端口的干扰,修改了 nginx 监听端口
1 | nmap -sS -p 20080,20443 localhost |
最后总结
- 抓包可以发现,nginx 拒绝握手是通过发送无效握手信息而不是直接关闭连接,所以在 nmap 扫描结果中还是能看到疑似 ssl 服务,这算是最后一点小遗憾吧
- 完成所有测试后,查看写入日志的信息,全部都是返回 444 响应而没有 400 响应了,这样就最大程度屏蔽了来自互联网的扫描