当前位置: 首页 > news >正文

Apache mod_evasive实战指南:精准拦截暴力扫描与高频CC攻击

1. 为什么是mod_evasive,而不是其他模块?

我第一次在客户现场处理突发流量时,服务器CPU直接飙到98%,Apache进程数暴涨到300+,top里全是httpd,netstat -an | grep :80 | wc -l显示连接数突破2800。查access.log发现同一IP在1秒内发起47次GET请求,路径全是/wp-login.php——典型的暴力爆破前置行为。当时第一反应是加iptables限速,但很快意识到:iptables工作在网络层,它拦不住已经建立的TCP连接;而Apache还在拼命fork子进程去响应这些恶意请求,资源早被耗尽了。

这时候mod_evasive的价值就凸显出来了:它工作在Apache应用层,能在请求进入PHP解析器之前就完成识别与拦截。它不是靠IP黑名单硬挡,而是基于三个维度实时计算——单位时间请求数、单IP并发连接数、同一URI访问频次。比如配置DOSPageCount 2,意味着同一个页面(如/index.html)在DOSPageInterval 1秒内被同一IP访问超过2次,就触发临时封禁。这个逻辑比单纯“每秒超10个包就DROP”更精准,因为它理解HTTP语义,能区分真实用户刷新和脚本扫库。

很多人误以为DDoS防护必须用商业WAF或云清洗服务,其实对中小站点,mod_evasive是成本最低、部署最快的首道防线。它不改变网络拓扑,不引入额外延迟,所有逻辑都在Apache进程内完成。我经手的56个WordPress站点中,有41个在启用mod_evasive后,暴力登录成功率从日均37次降到0——不是因为攻击消失了,而是攻击请求根本没机会触达PHP层就被403 Forbidden拦截了。它的核心优势在于“快准狠”:快——毫秒级响应;准——基于HTTP会话上下文判断;狠——直接返回403并记录日志,不给攻击者任何反馈。

提示:mod_evasive不是万能盾牌。它对SYN Flood这类网络层攻击无效,也无法防御CC攻击中模拟真人行为的慢速攻击(如Slowloris)。它的定位很明确:专治高频、无状态、重复路径的自动化扫描与爆破。如果你的服务器正被curl -s http://target.com/login.php?user=admin&pass=123这种脚本狂轰滥炸,mod_evasive就是你的第一剂退烧药。

2. 编译安装与模块加载的实操细节

很多教程直接告诉你a2enmod evasive,但现实是:Ubuntu/Debian官方源里的libapache2-mod-evasive版本普遍停留在1.10.1,而最新稳定版已是1.10.2,且修复了关键bug——比如在Apache 2.4.52+环境下,旧版会出现DOSHashTableSize参数被忽略的问题,导致哈希表溢出后性能断崖式下跌。所以,我坚持手动编译,全程可控。

先确认环境:apache2ctl -V | grep "Server version"输出Server version: Apache/2.4.58 (Ubuntu),说明是2.4.x系列。接着下载源码:

cd /tmp wget https://github.com/jzdziarski/mod_evasive/releases/download/v1.10.2/mod_evasive_1.10.2.tar.gz tar -xzf mod_evasive_1.10.2.tar.gz cd mod_evasive

关键来了:不要直接apxs -cia mod_evasive24.capxs会默认使用系统头文件路径,但Ubuntu的Apache开发包(apache2-dev)头文件实际在/usr/include/apache2,而apxs可能指向/usr/local/apache2/include。我踩过的坑是编译后httpd -M | grep evasive始终不显示模块,最后发现apxs -q INCLUDEDIR输出的是空值——因为apache2-dev没装。所以必须先执行:

sudo apt update && sudo apt install apache2-dev build-essential

再验证路径:

apxs -q INCLUDEDIR # 应输出 /usr/include/apache2 apxs -q LIBEXECDIR # 应输出 /usr/lib/apache2/modules

确认无误后编译:

sudo apxs -cia mod_evasive24.c

此时/usr/lib/apache2/modules/mod_evasive24.so已生成。但注意:模块名必须是mod_evasive24.so,不能是mod_evasive.so。因为Apache 2.4的模块命名规范强制要求带版本号后缀,否则LoadModule指令会报错Cannot load modules/mod_evasive.so into server: ... undefined symbol: ap_log_rerror。这是底层API变更导致的兼容性问题,旧文档常忽略这点。

加载模块有两种方式。推荐创建独立配置文件/etc/apache2/mods-available/evasive.load

<IfModule !mod_evasive24.c> LoadModule evasive24_module /usr/lib/apache2/modules/mod_evasive24.so </IfModule>

然后启用:sudo a2enmod evasive。为什么用<IfModule !mod_evasive24.c>包裹?因为如果模块加载失败,Apache启动时不会因LoadModule错误而崩溃,而是静默跳过,便于排查。我曾因SELinux策略阻止.so文件执行,用此方式避免整个Web服务瘫痪。

注意:编译后务必检查模块是否真被识别。执行sudo apache2ctl -M | grep evasive,输出应为evasive24_module (shared)。若显示evasive_module或无输出,说明模块名或路径有误,需重新编译。

3. 核心参数配置与阈值设定的工程化思维

参数配置不是填数字游戏,而是要结合你的业务特征做工程化权衡。比如DOSPageCount 2这个值,新手常照搬教程设为2,结果导致正常用户点两次刷新就被封。我建议用“三步法”确定合理阈值:

第一步:基线采集。在非高峰时段(如凌晨2-4点),用awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -nr | head -20统计TOP20 IP的请求频次。我维护的一个企业官网,TOP1 IP是百度爬虫,峰值QPS为8.3;而人工访问的IP,95%集中在1-3次/秒。这说明DOSPageCount下限可设为4。

第二步:攻击模拟。用ab -n 100 -c 10 http://your-site.com/(Apache Bench)模拟10并发请求100次,观察/var/log/apache2/evasive.log是否误报。当DOSPageCount=4时,ab测试触发率0%;但设为3时,10次测试中有2次误报。这验证了4是安全阈值。

第三步:动态调整。上线后监控DOSBlockingPeriod(封禁时长)的日志频率。如果evasive.log里每小时出现200+条Blacklisting address xxx.xxx.xxx.xxx,说明阈值过严;若一周只出现3条,则说明太松。我的经验是:初始值设为基线值的2倍,再根据误报率微调

以下是生产环境验证过的参数组合(/etc/apache2/mods-available/evasive.conf):

<IfModule mod_evasive24.c> DOSLogDir "/var/log/apache2" DOSHashTableSize 3097 DOSPageCount 6 DOSSiteCount 100 DOSPageInterval 1 DOSSiteInterval 1 DOSBlockingPeriod 600 DOSBlockingPage "/403.html" DOSEmailNotify admin@your-domain.com DOSWhitelist 127.0.0.1 DOSWhitelist 192.168.1.* </IfModule>

逐条解释其工程意义:

  • DOSHashTableSize 3097:哈希表大小必须是质数。3097是经过压力测试的最优值——小于3000时,高并发下哈希冲突率超15%,导致CPU占用飙升;大于3500则内存占用陡增,对1G内存VPS不友好。
  • DOSPageCount 6:同一页面1秒内最多6次请求。为什么不是2?因为现代SPA应用(如Vue/React)首页加载会触发/api/user,/api/config等多次AJAX,6次足够覆盖正常交互。
  • DOSSiteCount 100:同一IP全站1秒内最多100次请求。这是防CC攻击的关键,设为100既能拦住curl脚本(通常QPS>200),又不影响CDN回源(Cloudflare回源QPS约80)。
  • DOSBlockingPeriod 600:封禁10分钟。太短(如60秒)会让攻击者轻松绕过;太长(如86400)则可能误伤动态IP用户。10分钟是平衡点——足够让脚本暂停,又不至于影响真实用户。

实测心得:DOSBlockingPage指定的/403.html必须放在DocumentRoot下,且不能是PHP动态页。我曾设为/403.php,结果攻击者通过curl -H "User-Agent: Mozilla" http://site.com/403.php反复请求,反而把403页变成了新的攻击入口。静态HTML最稳妥。

4. 日志分析与误报排查的完整链路

日志不是摆设,而是你和攻击者博弈的战场地图。/var/log/apache2/evasive.log里每一行都包含关键线索,但默认格式太简陋。比如一条典型日志:

[Mon Jun 10 14:22:31 2024] Blacklisting address 203.208.60.123 for 600 seconds

它只告诉你IP被封,却没说为什么被封。要深挖根因,必须改造日志输出。在evasive.conf中添加:

DOSLogDir "/var/log/apache2" DOSLogFormat "%t %a %v %U %q %r"

重启Apache后,日志变成:

[Mon Jun 10 14:22:31 2024] 203.208.60.123 example.com /wp-login.php ?user=admin&pass=123 "GET /wp-login.php?user=admin&pass=123 HTTP/1.1"

现在就能看到:攻击者在爆破WordPress后台!这比单纯封IP更有价值——你可以立刻检查/wp-login.php是否暴露在公网,或是否启用了强密码策略。

但更关键的是识别误报。上周一个客户投诉“公司IP被封无法访问”,我查evasive.log发现:

[Wed Jun 12 09:15:02 2024] Blacklisting address 192.168.5.100 for 600 seconds

192.168.5.100是内网IP,按理说在DOSWhitelist里。但DOSWhitelist 192.168.5.*写成了DOSWhitelist 192.168.5.(少了个星号)。这种低级错误在配置文件里极难发现。我的排查流程是:

  1. 确认IP归属grep "192.168.5.100" /var/log/apache2/access.log | tail -5,发现全是GET /admin/dashboard HTTP/1.1,说明是内部管理员在操作。
  2. 检查白名单语法sudo apache2ctl -t -D DUMP_MODULES | grep evasive确认模块已加载,再sudo cat /etc/apache2/mods-available/evasive.conf | grep DOSWhitelist,果然发现语法错误。
  3. 验证修复效果:修改后执行sudo apache2ctl configtest,输出Syntax OK,再sudo systemctl reload apache2
  4. 主动测试:用另一台内网机器curl -I http://your-site.com/admin,确认返回200而非403。

另一个高频误报场景是CDN穿透。某次客户用Cloudflare,但源站Apache日志里%a显示的是Cloudflare节点IP(如173.245.48.0),导致所有请求都被归到少数几个IP下。解决方案是启用mod_remoteip,在evasive.conf前加载:

RemoteIPHeader X-Forwarded-For RemoteIPInternalProxy 173.245.48.0/20 103.21.244.0/22

这样%a就变成真实用户IP,mod_evasive才能精准拦截。我统计过,未启用mod_remoteip时,误报率高达38%;启用后降至1.2%。

关键技巧:用awk '$3 ~ /203\.208\.60\./ {print $0}' /var/log/apache2/evasive.log | wc -l快速统计某IP段被封次数。如果单日超50次,大概率是真实攻击,可同步加入iptables永久封禁:sudo iptables -A INPUT -s 203.208.60.123 -j DROP

5. 与fail2ban联动实现多层防御闭环

mod_evasive解决的是“应用层瞬时洪流”,但攻击者可能换个IP继续打。这时需要fail2ban把临时封禁升级为系统级持久封禁。两者联动不是简单拼接,而是要有数据管道和状态同步。

首先,让mod_evasive把封禁事件写入专用日志。修改evasive.conf

DOSLogDir "/var/log/apache2" DOSLogFormat "%t %a %v %U %q %r" DOSBlockingPage "/403.html" # 新增:将封禁事件写入独立日志 DOSLogDir "/var/log/apache2" DOSLogFormat "%t %a %v %U %q %r" CustomLog "/var/log/apache2/evasive_access.log" "%t %a %v %U %q %r" env=DOS_BLOCKED

这里用env=DOS_BLOCKED标记被拦截的请求,需在mod_evasive源码中打补丁(见后文)。但更简单的方法是:直接解析evasive.log。创建fail2ban过滤器/etc/fail2ban/filter.d/apache-evasive.conf

[Definition] failregex = ^\[.*?\] Blacklisting address <HOST> for \d+ seconds$ ignoreregex =

然后创建jail配置/etc/fail2ban/jail.local

[apache-evasive] enabled = true filter = apache-evasive logpath = /var/log/apache2/evasive.log maxretry = 3 bantime = 86400 findtime = 600 action = iptables[name=HTTP, port=http, protocol=tcp]

关键参数解读:

  • maxretry = 3:同一IP在10分钟(findtime)内被mod_evasive封禁3次,就触发fail2ban永久拉黑。
  • bantime = 86400:封禁24小时,避免长期封禁影响业务。
  • action = iptables[...]:直接操作iptables,比ufw更轻量,适合高并发场景。

但有个致命陷阱:fail2ban默认每分钟扫描一次日志,而mod_evasive封禁是毫秒级的。如果攻击者在1秒内触发5次封禁,fail2ban可能只捕获到其中1次,导致漏判。解决方案是修改/etc/fail2ban/jail.conf中的polling = true,强制fail2ban用inotify实时监听日志变化:

[DEFAULT] backend = auto polling = true

重启fail2ban:sudo systemctl restart fail2ban

验证联动是否生效:用ab -n 50 -c 20 http://your-site.com/发起压测,然后执行:

sudo fail2ban-client status apache-evasive # 输出应类似: # Status for the jail: apache-evasive # |- Filter # | |- Currently failed: 0 # | |- Total failed: 5 # | `- File list: /var/log/apache2/evasive.log # `- Actions # |- Currently banned: 1 # |- Total banned: 1 # `- Banned IP list: 192.168.1.100

此时iptables -L -n | grep 192.168.1.100应显示该IP被DROP。

经验之谈:联动后要监控fail2ban的CPU占用。我曾因findtime设为300秒(5分钟)导致fail2ban每秒解析上万行日志,CPU飙到90%。最终将findtime调至600秒,并添加ignoreip = 127.0.0.1/32 192.168.0.0/16排除内网,CPU回落至5%以下。

6. 生产环境避坑指南与性能调优实录

部署不是终点,而是运维的起点。我在12个生产环境踩过的坑,总结成这份血泪清单:

坑1:日志目录权限错误
DOSLogDir "/var/log/apache2"要求Apache用户(www-data)对该目录有写权限。但/var/log/apache2默认属主是root,导致evasive.log无法创建,错误日志里全是Permission denied。解决方案:

sudo mkdir -p /var/log/apache2/evasive sudo chown www-data:www-data /var/log/apache2/evasive sudo chmod 755 /var/log/apache2/evasive

并在evasive.conf中改为DOSLogDir "/var/log/apache2/evasive"

坑2:哈希表内存溢出
DOSHashTableSize设得过大(如10007)会导致Apache启动时分配过多内存。在512MB内存VPS上,httpd -t会报错Cannot allocate memory。我的实测数据:

哈希表大小内存占用适用场景
20391.2MB100并发以下
30971.8MB中小企业官网
50032.9MB高流量电商
超过5003后,内存增长与性能提升不成正比,反而增加GC压力。

坑3:SELinux阻止模块加载
CentOS/RHEL系统默认开启SELinux,mod_evasive24.so会被标记为unconfined_u:object_r:lib_t:s0,而Apache要求system_u:object_r:httpd_modules_t:s0。执行:

sudo semanage fcontext -a -t httpd_modules_t "/usr/lib64/httpd/modules/mod_evasive24.so" sudo restorecon -v "/usr/lib64/httpd/modules/mod_evasive24.so"

坑4:与mod_security冲突
如果同时启用mod_security,两者都拦截403请求,会导致日志混乱。解决方案是让mod_security放行mod_evasive的拦截:在modsecurity.conf中添加:

SecRule REQUEST_HEADERS:User-Agent "mod_evasive" "id:1001,phase:1,pass,nolog,tag:'OWASP_CRS'"

并在mod_evasive的DOSBlockingPage中设置<meta name="generator" content="mod_evasive">,方便mod_security识别。

性能调优实录
在一台4核8G的WordPress服务器上,启用mod_evasive后,我做了三次压测:

  • 未启用:ab -n 1000 -c 100,平均响应时间428ms,错误率12%
  • 启用默认参数:响应时间降至215ms,错误率0%,但htop显示Apache进程CPU占用78%
  • 启用优化参数(DOSHashTableSize 3097,DOSPageCount 6,DOSSiteCount 100):响应时间189ms,CPU占用52%,QPS从210提升至340

关键优化点是DOSPageIntervalDOSSiteInterval都设为1秒——缩短检测窗口,让拦截更及时。但要注意:设为0.5秒会导致高并发下哈希表锁竞争加剧,反而降低吞吐量。

最后提醒:定期清理evasive.log。我用logrotate管理,/etc/logrotate.d/apache2-evasive内容如下:

/var/log/apache2/evasive.log { daily missingok rotate 30 compress delaycompress notifempty create 644 www-data www-data sharedscripts postrotate if [ -f " /var/run/apache2/apache2.pid" ]; then /usr/sbin/invoke-rc.d apache2 reload > /dev/null fi endscript }

这样既保证日志可追溯,又避免磁盘被占满。

http://www.zskr.cn/news/1388538.html

相关文章:

  • NVIDIA Profile Inspector完全手册:解锁显卡隐藏性能的终极指南
  • YOLO26在大豆目标检测中的极致性能:召回率0.996、推理速度6.0ms/张
  • 2026年选购电缆故障定点仪,认准专业生产的优质源头厂家
  • Excel排序本质:数据关系重建与业务逻辑落地
  • 保姆级教程:用ESP32-CAM和Python OpenCV搭建一个简易家庭监控(RTSP推拉流实战)
  • 2026年开关插座哪个品牌性价比高?五大品牌真实口碑测评 - 品牌排行榜
  • Bifrost Code Mode:重构LLM工具调用范式,实现Token成本减半与延迟降低
  • Python模拟浏览器环境绕过Cloudflare 5秒盾
  • 金融领域多模态RAG框架MultiFinRAG解析与应用
  • NMOS管、PMOS管
  • Kotlin协程作用域实战避坑指南:coroutineScope、supervisorScope与withContext到底怎么选?
  • DeepSeek LeetCode 2646. 最小化旅行的价格总和 C++实现
  • 2026年北京朝阳区搬家公司排行榜多维度测评推荐+避坑指南 - 余小铁
  • 杰理701N SDK蓝牙回连实战:从可视化配置到代码调试,手把手教你搞定耳机断连重连
  • 中国医学科学研究院考研辅导班靠谱推荐:高性价比与良好口碑实力选择 - michalwang
  • 2026年京东云618活动时间、活动入口、优惠活动详细解读
  • 告别make menuconfig:深度解析U-Boot源码目录结构与Makefile的编译奥秘
  • 鸿蒙Hi3861开发板智能小车项目拆解:STM32驱动板、JSON通信协议与微信小程序端是怎么联动的?
  • CentOS7 OpenSSL 1.1.1 ABI冲突与安全隔离部署指南
  • 2026 年福建莆田全屋高端定制家居设计与选材选型指南
  • 为自托管AI构建安全Shell沙盒:Docker容器隔离实践
  • 构建低成本高可用网络爬虫系统:从架构设计到成本控制实战
  • 读书笔记 GenAI FinOps vs. Cloud FinOps:同根同源,挑战各异
  • 安全攻防 - 03 TLCP 握手:双证书、密码套件与常见术语
  • 2026年岳阳市正规上门黄金白银回收品牌门店名录 K金+铂金+金条+银条回收门店联系方式推荐+指南 - 盛世金银回收
  • 网站上线两个月,360和必应就是不收录?我是怎么靠蜘蛛池把这事翻盘的
  • 别再盲选大模型了!DeepSeek-V2/V3/R1在中文长文本、代码生成、数学推理三类场景的TOP-1准确率差距高达23.6%,你用对版本了吗?
  • 01-认知篇-总览-HybridCLR是什么
  • 创客匠人:当知识付费遇上AI:学习这件事正在悄悄改变
  • iOS真机自动化测试连不上?WebDriverAgent签名与Appium配置深度解析