昨天我记录本站正遭受一场大规模的组合式攻击,手法是“伪装 UA + 全球分布 + 高并发探测”叠加而成。经过一系列尝试,最后我写了个脚本,把所有来访 IP 实时记录进了 visitors.log
,然后筛查那些访问次数超过 200 的 IP 和反代,批量加进宝塔 WAF 黑名单…我以为这种方式可以在尽量不误封正常用户的前提下控制住局面。
我陆续封了数百个 IP,但一直到晚上攻击不降反升,反而愈演愈烈……只能放弃人工方式,转而启用自动化封锁机制。
🧱 应对策略:自动化黑名单封锁 + ipset 动态引入
既然手动拉黑根本跟不上节奏,那就必须自动化。我的做法是结合 ipset
和 iptables
实现动态黑名单机制,效率高,性能影响小,还能随时增删 IP,无需重载防火墙规则。首先,我创建了一个 IP 集合并加入到防火墙:
ipset create blocked_ips hash:ip
iptables -I INPUT -m set --match-set blocked_ips src -j DROP
接下来,我只需要定期提取高频访问 IP,把它们写入 ip_black.txt
,然后用一行命令动态加入黑名单:
while read -r ip; do ipset add blocked_ips "$ip" -exist; done < /root/ip_black.txt
相比起一条条写规则,ipset
是专门为这种场景设计的,瞬间封几百上千个 IP 毫无压力。配合我的访问日志脚本,整个封锁流程几乎可以做到半自动+批量处理,省时省力。当然在这个过程中,要把操作电脑的公网 IP、内网循环 IP 等放通,并且加入到自动防护脚本的白名单中。完整脚本如下:
#!/bin/bash
LOG="/root/visitor-log.txt"
TMPFILE="/root/ip_hits.tmp"
THRESHOLD=200
WHITELIST=("123.146.49.x" "100.100.100.200" "172.16.13.182" "39.144.218.x")
NOW=$(date +%s)
CUTOFF=$(date -d "-10 min" +%s)
# 提取最近 10 分钟的 IP
awk -v now="$NOW" -v cutoff="$CUTOFF" '
{
# 匹配时间戳并转换
if (match($0, /^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}/, t)) {
gsub(/[-:]/, " ", t[0])
logtime = mktime(t[0])
if (logtime >= cutoff && logtime <= now) {
# 提取 => 后面的 IP(忽略括号内容)
if (match($0, /=> ([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)/, ip))
print ip[1]
}
}
}
' "$LOG" | sort | uniq -c | awk -v t=$THRESHOLD '$1 > t {print $2}' > "$TMPFILE"
# 当前 ipset 集合
ipset list blocked_ips -output plain | awk '/^Members:/{flag=1; next} flag' > /tmp/ipset_existing.txt
declare -A blocked
while read -r ip; do
blocked["$ip"]=1
done < /tmp/ipset_existing.txt
# 添加高频 IP(排除白名单)
while read -r ip; do
if [[ -z "${blocked[$ip]}" && ! " ${WHITELIST[@]} " =~ " $ip " ]]; then
ipset add blocked_ips "$ip" -exist
echo "Blocked $ip"
fi
done < "$TMPFILE"
注意,上述脚本须配合上文「救救我!网站遭受大规模伪装 UA 恶意流量攻击」中提到的实时记录访问日志脚本log-visitors.sh
和定时任务配合执行,其中的访问数量和放通 IP 应酌情调整。
🧪 效果监控与持续迭代
随着黑名单的扩充,系统负载特别是实时连接数明显下降了,503 几率也大幅降低。截至目前,最终封锁的 IP 达到 1500 个,而这个数字还在持续不断增加中!在这个过程中,我学到了一些命令,比如:
iptables -L -n -v --line-numbers # 查看防火墙列表
ipset list blocked_ips # 查看被禁用的 IP 表
ipset save > /etc/ipset.conf # 存储自己的 IP 表,以防重启丢失
就这么一两天的折腾,我似乎都会一些服务器安全运维了…还做了一些小脚本,比如服务器实时 TCP 检测:
#!/bin/bash
while true; do
clear
now=$(date '+%F %T')
total=$(ss -tan | grep -vE '^State' | wc -l)
echo "========= 🌐 当前连接概览($now) ========="
echo
echo "👉 当前总 TCP 连接数:$total"
echo
echo "👉 当前 TCP 连接状态统计:"
ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -nr
echo
echo "👉 各 IP 当前连接数(所有 TCP 状态):"
ss -tan | awk 'NR>1 {print $5}' | grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' \
| sort | uniq -c | sort -nr | head -n 20
echo
sleep 3
done
通过这个脚本,我注意到我的服务器实时连接数几乎从未低于 100,哪怕在凌晨低峰期也持续高位运行,可见这波攻击压根就没停过……只能说服了。


🧠 论持久战?接下来的考虑
攻击仍在持续,而各位也知道,我的服务器是按量计费的。为了控住成本,我下调了带宽上限,但问题并不在于“高流量冲击”,而是大量遍布全球的小流量持续嗅探——广撒网、低频率、高并发,极难防御。对我这种个人运维来说,说实话真想不出更好的应对办法了。哪怕已经限流、封 IP,最近几天的出口流量依然是平时的数倍。这攻击就像在“养蛊”,一点点榨干资源,拖着系统往下走……真的是,有点招架不住了。
接下来我会逐步放宽访问限制,比如目前已经把最初那个输入验证码的人机校验模式,改成了点击按钮 + 鼠标移动侦测,主要是为了防脚本、防模拟浏览器。虽然简化了流程,但依然能拦住一部分“假人”。不过面对这类持续、分布式的小流量骚扰,说实话我也在边做边学……如果你有更优雅、更实用的防御思路,还请赐教!🙏
是不是套个CDN好一点,做好策略,回源流量就不多,按量付费的服务器也就划算一点