阻止针对 NGINX 的恶意访问
为了对付爬虫以及各种攻击,起初我选择自己编写 Bash 脚本定时监控 Nginx 日志。
问题是对 Nginx 不管不顾可能导致恶意访问在被封禁之前已经得逞。
另外写正则并不是一个愉快的过程,我的目的是识别出恶意访问,然后阻止这些请求并封禁 IP。
经过一段时间的摸索,关键词就是 444 以及 Fail2ban。
环境需求
- Nginx - 需要自己 编译。
- Naxsi - 开源的 Nginx WAF,与 Nginx 一起 编译。
- Nginx GeoIP2 module - 请认准这是 GeoIP2,使 Nginx 识别 IP 所在的国家和城市,同样与 Nginx 一起 编译。
背景知识
请求日志
首先启动人眼观察,从 Nginx 的 access_log 中摘取一次典型的恶意访问。
因为我已经完成了本文的工作,所以它的响应是 444,一般来说是 404。
1 | 118.42.xxx.xx - - [02/Feb/2019:08:23:40 +0800] "GET /phpMyAdmion/index.php HTTP/1.1" 444 0 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:52.0) Gecko/20100101 Firefox/52.0" |
每一行的格式都是这样的(没有自定义 log_format 的话)。
1 | $remote_addr - - [$time_local] "$request" $status $body_bytes_sent "$http_referer" "$http_user_agent" |
显然我们可以给 $remote_addr、$request、$http_referer、$http_user_agent 加上一层判定再做出设置好的响应,翻译过来就是根据远程 IP、请求、请求来源、用户代理做出相同(一网打尽)或不同(个性化)的处理。
我的方案是一网打尽,通过一系列的配置,最终表现为 access_log 中的恶意访问的响应为统一的 444,然后使用 Fail2ban 监控请求日志实现 IP 封禁。
错误日志
这里真正涉及的是 naxsilogs 也就是 Naxsi 日志,naxsilogs 写入至 Nginx 的 error_log。
我将 Naxsi 规则触发后的响应同样设置为 444,此时这一类请求并不会写入 Nginx 的 access_log 而只会写入 error_log,大概过程如下:
1 | 恶意访问触发 Naxsi ------> 444 响应 ------> 写入 error.log |
参考上文,同样配合 Fail2ban 使用,只不过这一次监控的日志并不一样。
模块与指令
Fail2ban
我对 Fail2ban 没什么深入体验但不妨碍使用,基本上能遇到的问题都可以搜索到答案。
之前对 Fail2ban 的印象停留在 SSH 防爆破,特别是我使用 sshguard 以后就更加没接触过它。
Fail2ban 可以监控各式各样的日志,依据你设定的规则控制 iptables 实现 IP 封禁。
安装过后更是有很多配置可以选用,涵盖 Apache、Nginx、HAProxy 等等。
就本文来说,新增两个 jail 以及 filter 就足够了。
Nginx 设置
主配置
nginx.conf
1 | ... |
用户代理规则
UA.rules
1 | map $http_user_agent $UA_Policy { |
国别规则
GeoIP2.rules | GeoIP2 采用 ISO 3166-2 代码
1 | # 匹配国家,请调整为自己想要的配置或者干脆忽略 |
额外规则
extra.rules
1 | # 非 GET HEAD POST 请求一律 444 |
Naxsi 规则
naxsi.rules | 不同于 Naxsi 核心规则 | 参见 官方说明
1 | SecRulesEnabled; |
站点配置
vhost/*.conf
1 | # 无关配置不再赘述 |
Fail2ban 设置
警告
重要的警告放前面,因为我已经完成了各种测试,自我感觉方案成熟所以将封禁设为 72 小时。
如果你要测试(你肯定得测试),请务必将 bantime 设为短时长比如 1m(一分钟),否则你的测试 IP 可能一键去世。
监控 access_log
- 新增 444 监狱
1 | cat >> /etc/fail2ban/jail.d/nginx-444.conf << EOF |
- 新增 444 匹配
1 | cat >> /etc/fail2ban/filter.d/nginx-444.conf << EOF |
监控 error_log
- 新增 naxsi 监狱
1 | cat >> /etc/fail2ban/jail.d/nginx-naxsi.conf << EOF |
- 新增 naxsi 匹配
1 | cat >> /etc/fail2ban/filter.d/nginx-naxsi.conf << EOF |
重启/验证 Fail2ban
1 | fail2ban-client --test |
测试
假设你按部就班地布置好一切,可以通过 curl 触发封禁。
再次警告,请务必将 bantime 设置为短时长比如 1m。
另外,在确认测试结果良好后,你可能需要删除 Fail2ban 数据库并复原 bantime 设置最后再次重启 Fail2ban。
本地操作
1 | # 正常访问你的网站,Nginx 正常响应 |
远程操作
- 验证
1 | $ tail -f /var/log/fail2ban.log |
- 恢复
1 | service fail2ban stop |
小结
- Nginx 本身支持 deny IP,我没有采用这种方式。
- Fail2ban 单独使用也可以封禁恶意访问,它的本质就是读取日志然后进行防火墙操作。
- iptables 也不是唯一选择,还支持 nftables、firewalld 等等。
- 限流方面的配置暂时没有涉及,以后有机会再行补充。