去年十月,网站一个做电商的黑名朋友半夜给我打电话,说网站后台被境外IP刷了六千多笔恶意订单,单设每笔金额都是网站0.01元。他问我怎么封IP,黑名我让他先去服务器防火墙加规则。单设结果他折腾到凌晨三点,网站把整个机房IP段全封了,黑名连自己公司的单设办公网都进不去。这事说明一个问题:黑名单不是网站拉得越狠越好,得知道往哪儿拉、黑名怎么拉。单设
网站黑名单分几个层级。网站最外层是黑名服务器防火墙,比如iptables或者云服务商的单设安全组。这里适合封那些明显异常的IP,比如短时间内发起大量请求的。我自己的博客去年被DDoS,每秒三万次请求,全来自东欧几个机房。直接在安全组里把那些C段封了,五分钟搞定。但别学我那个朋友,封之前先查清楚IP归属,别把自己人误伤。
中间层是Web服务器本身。Nginx有个deny指令,写在配置文件里就行。比如deny 192.168.1.100;,重启服务生效。Apache也有类似的Require not ip。这层适合封单个IP或者小范围IP段。有个细节:Nginx的deny是按顺序匹配的,如果你先allow了某个IP再deny整个段,那个IP还是能进来。顺序错了等于白写。
再往上是应用层。WordPress有插件可以封IP,比如Wordfence。但我不太推荐全靠插件,因为插件本身也消耗资源。更稳的做法是在代码里判断。比如PHP里写个函数,从数据库读黑名单,每次请求先查一下。我见过一个站,黑名单表里存了二十万条IP,每次查询都全表扫描,结果正常用户访问也卡。后来加了索引,又改成Redis缓存,才把响应时间压到50毫秒以内。
黑名单的触发条件比黑名单本身更重要。有人喜欢用“访问频率超过100次/分钟”就封,这太粗暴。正常用户点开一个图库页面,可能瞬间加载几十张图,加上CDN回源,很容易误判。我一般看三个指标:请求间隔是否规律、User-Agent是否异常、是否只请求特定路径。比如一个IP每秒钟请求一次/wp-login.php,连续十分钟,那肯定是暴力破解,直接封。如果只是访问文章页,频率高一点也正常。
自动封禁要留后路。我见过有人写脚本,检测到异常就调云服务商API加黑名单,结果脚本自己出了bug,把整个办公室的出口IP封了。所以自动封禁最好设个有效期,比如封24小时自动解封。手动封禁也要留个白名单入口,至少让管理员能通过SSH或者后台进去。我自己的做法是:防火墙层封永久,应用层封24小时,数据库层只记录不封禁,人工审核后再决定。
还有个容易忽略的点:IPv6。很多人只封IPv4,结果攻击者用IPv6照样进来。现在云服务器基本都支持双栈,黑名单里得同时存两种格式。另外,CDN后面的真实IP是藏在X-Forwarded-For头里的,如果你直接封CDN节点IP,等于把整个CDN都封了。得先配置好real_ip模块,拿到真实客户端IP再封。
最后说个实际案例。有个论坛被广告机盯上,每分钟发几十条垃圾帖。管理员封了IP,对方换一个继续。后来他发现这些IP都来自同一个ASN,直接在防火墙里封了整个ASN,世界清净了。但代价是那个ASN里也有几个正常用户,他们换了网络才能访问。所以封ASN是核武器,不到万不得已别用。一般情况,封C段就够了。
黑名单是个动态维护的东西。我每周会花十分钟看一下封禁日志,把误封的解掉,把新出现的恶意IP段加进去。别指望设一次就一劳永逸。网站活着,攻防就在继续。