找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
教程经验总索引TVMW5/小日本5 索引【视频转换】小日本4/TE4XP 索引【视频转换】TAW4 / TDA3 索引【DVD打包软件】
Nero 索引DVD-Lab 索引【DVD打包软件】编解码器 索引 【必读】固顶帖011号
查看: 110|回复: 0

[经验] 【实战分享】基于 Nginx + Fail2ban 的 Discuz! X 高级防刷方案(精准拦截恶意收藏、搜索爆破、WP 扫描器)

[复制链接]
  • 打卡等级:热心大叔

9335

威望

2724

金钱

1万

贡献

管理员

自由的灵魂

积分
102539
主题
5370
回帖
26389
注册时间
2003-4-10
最后登录
2026-1-18
发表于 2025-12-15 01:31:19 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
大家好!
近期我的 Discuz! X 站点(www.dianbo.org)频繁遭遇自动化脚本攻击,包括:
  • 恶意刷 /search.php?source=hotsearch 导致 CPU 飙升
  • 自动化脚本批量“收藏”帖子或板块(/home.php?ac=favorite),无 Referer 或自引用
  • 大量 WordPress 路径扫描(如 wp-login.php, xmlrpc.php)
  • 高频 PHP 请求试探漏洞

经过一段时间优化,我构建了一套 Nginx 层精准拦截 + Fail2ban 动态封禁 的组合防御体系,效果显著:日均拦截异常请求 数万次,服务器负载明显下降。


现将完整配置开源分享,欢迎交流优化建议!




一、Nginx 核心防护配置(基于宝塔环境)
环境说明:宝塔面板 + Nginx 1.26 + PHP 8.2(Unix Socket: /tmp/php-cgi-82.sock)
1. 通用限流与判断逻辑(置于 nginx.conf 的 http{} 块中)
  1. # 优化大黑名单性能(支持 15w+ IP)
  2. map_hash_max_size 262144;
  3. map_hash_bucket_size 256;

  4. # Fail2ban 持久化黑名单(可选)
  5. map $remote_addr $blocked_ip {
  6.     default 0;
  7.     include /www/fail2ban-persist/bad-ips.map;
  8. }

  9. # 判断是否为收藏操作
  10. map $args $is_favorite {
  11.     default 0;
  12.     ~(^|&)ac=favorite(&|$) 1;
  13. }

  14. # 判断 Referer 是否为空
  15. map $http_referer $is_empty_referer {
  16.     default 0;
  17.     "" 1;
  18. }

  19. # 判断是否为自引用(Referer 与当前 URI 相同)
  20. map "$http_referer|$request_uri" $is_self_referer {
  21.     default 0;
  22.     "~*^https?://[^/]+(/home\.php\?.+)\|\1$" 1;
  23. }

  24. # 限流 Zone 定义
  25. limit_req_zone $binary_remote_addr zone=search:10m rate=10r/s;
  26. limit_req_zone $binary_remote_addr zone=test:10m rate=1r/s;
  27. limit_req_zone $server_name zone=search_total:10m rate=30r/s;
  28. limit_req_zone $binary_remote_addr zone=general:10m rate=5r/s;
  29. limit_req_zone $binary_remote_addr zone=login:10m rate=2r/s;
  30. limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s;
  31. limit_req_zone $binary_remote_addr zone=attachment:10m rate=3r/s;
  32. limit_req_zone $binary_remote_addr zone=malicious:10m rate=3r/s;  # 高危请求
  33. limit_conn_zone $binary_remote_addr zone=conn_per_ip:10m;

  34. # 日志辅助配置(宝塔默认路径)
  35. include /www/server/nginx/conf/log_filter.conf;
  36. include /www/server/nginx/conf/404_map.conf;
复制代码




2. 精准站点规则(置于站点配置文件,如 www.dianbo.org.conf)
  1. # 🔍 搜索接口防刷(独立限流 + 屏蔽恶意参数)
  2. location = /search.php {
  3.     if ($args ~* "(^|&)source=hotsearch(&|$)") {
  4.         access_log off;
  5.         return 444;  # 静默丢弃
  6.     }
  7.     limit_req zone=search burst=5 nodelay;
  8.     limit_req zone=search_total burst=10;
  9.     fastcgi_pass unix:/tmp/php-cgi-82.sock;
  10.     fastcgi_index index.php;
  11.     include fastcgi.conf;
  12.     fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/";
  13. }

  14. # ❤️ 收藏操作防护(防自动化脚本)
  15. location = /home.php {
  16.     set $should_block "";
  17.     if ($is_favorite = "1") { set $should_block "F"; }
  18.     if ($is_empty_referer = "1") { set $should_block "${should_block}E"; }
  19.     if ($is_self_referer = "1") { set $should_block "${should_block}S"; }

  20.     # 拦截:收藏 + 无 Referer(FE) 或 收藏 + 自引用(FS)
  21.     if ($should_block = "FE") { access_log off; return 444; }
  22.     if ($should_block = "FS") { access_log off; return 444; }

  23.     include fastcgi.conf;
  24.     fastcgi_pass unix:/tmp/php-cgi-82.sock;
  25.     fastcgi_index index.php;
  26.     fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/";
  27. }

  28. # 🖼️ 用户头像免限流(确保体验)
  29. location = /uc_server/avatar.php {
  30.     fastcgi_pass unix:/tmp/php-cgi-82.sock;
  31.     include fastcgi.conf;
  32.     fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/";
  33.     # 注意:此处未加 limit_req
  34. }

  35. # 🚫 静默拦截 WordPress 扫描器(Discuz 不需要这些路径)
  36. location ~* ^/(?:xmlrpc\.php|wp-login\.php|wp-admin|wp-config\.php|wp-content|wp-includes|wp-json|\.env|readme\.html|license\.txt)(?:$|/) {
  37.     access_log off;
  38.     log_not_found off;
  39.     return 444;
  40. }
复制代码

💡 说明:return 444 是 Nginx 特有指令,直接关闭连接且不返回任何响应,有效节省资源。
经验:能用 Nginx 拦截的话,尽量不用 Fail2ban,以节省 PHP 开销。Fail2ban 仅做兜底。


二、Fail2ban 动态封禁策略1. jail.local 配置
  1. [DEFAULT]
  2. ignoreip = 127.0.0.1/8
  3. # 如需添加可信运维 IP,请在上一行后面追加,例如:
  4. # ignoreip = 127.0.0.1/8 203.0.113.100
  5. bantime = 1d
  6. findtime = 300
  7. maxretry = 2
  8. banaction = iptables-ipset
  9. logpath_discuz = /www/wwwlogs/www.dianbo.org.log

  10. [sshd-ipset]
  11. enabled = true
  12. port = ssh,26444
  13. logpath = /var/log/auth.log
  14. maxretry = 2

  15. [nginxwpscan-ipset]
  16. enabled = true
  17. filter = nginxwpscan-ipset
  18. logpath = %(logpath_discuz)s
  19. maxretry = 3
  20. findtime = 1h

  21. [nginxmalicious-ipset]
  22. enabled = true
  23. filter = nginxmalicious-ipset
  24. logpath = %(logpath_discuz)s
  25. maxretry = 1
  26. findtime = 1d

  27. [search-ipset]
  28. enabled = true
  29. filter = search-ipset
  30. logpath = %(logpath_discuz)s
  31. maxretry = 1
  32. findtime = 600
复制代码

2. 示例 Filter:search-ipset.conf 用于检测异常中断或高频失败请求:
  1. # /etc/fail2ban/filter.d/search-ipset.conf
  2. [Definition]
  3. datepattern = ^\[%%d/%%b/%%Y:%%H:%%M:%%S

  4. failregex = ^<HOST> - - \[.*\] "(GET|POST) /+(search|home|member|misc|portal)\.php\?[^"]*formhash=[a-f0-9]{8}[^"]*" (?:\d{3} )?503
  5.             ^<HOST> - - \[.*\] "POST /member\.php\?mod=logging&action=login[^"]*" 499 \d+
  6.             ^<HOST> - - \[.*\] "GET /misc\.php\?mod=seccode&[^"]*" 503 \d+

  7. ignoreregex =
复制代码
以上仅为示例,请从自己的网站日志提取专属攻击特征,构造自己的专属规则。



三、效果与建议
  • CPU 负载下降 40%+:静默丢弃无效请求,减少 PHP-FPM 压力
  • 自动化脚本基本失效:收藏、搜索刷量归零
  • 日志干净:WP 扫描器不再污染 access.log
  • ⚠️ 建议
    • 定期清理 /www/fail2ban-persist/bad-ips.map(可配合 cron)
    • 对 /member.php 登录接口单独限流(如 zone=login)
    • 结合 Cloudflare 等 CDN 可进一步前置过滤



四、结语

这套方案已在生产环境稳定运行接近一个月,兼顾安全性用户体验。所有配置均基于宝塔默认路径,可直接复用。

欢迎各位站长测试、反馈,也期待看到大家的优化思路!

附:我的站点 www.dianbo.org 已启用上述防护,欢迎“友好探测” 😄
提醒:防范不能一劳永逸,需要时刻关注网站日志,以发现新的威胁,及时更新防护规则。




另:为了监控防范效果,制作了一个网站安全状态仪表盘,放在页面右下角:

截图202512150121427379.jpg
点开效果如图:
截图202512150122461295.jpg
在攻击最高的一些日子,每天新增3万多个IP,一个多星期时间持久化黑名单已经封顶到50万,于是索性关了热搜功能(清空所有热搜词),然后在Nginx层对热搜直接进行444拦截,图上的数据一下子清静了。
如果你想了解持久化黑名单的设置,欢迎回帖交流。



IsaacZ的发言中如果提到任何字母缩写或专有名词,在本论坛中一般都能搜索到。点此立即搜索点拨论坛。如有搜索不到的,请尝试问AI,或者跟帖提问。
发帖前请注意看置顶帖。如果你发现自己的问题没有得到回答,说明你的问题提错了地方或者提问前未先阅读版规。
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|Archiver|手机版|小黑屋|点拨论坛 |网站地图|网站地图🛡️ 实时安全防护状态

GMT+8, 2026-1-18 22:41 , Processed in 0.058242 second(s), 7 queries , Redis On.

Powered by Discuz! X3.5

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表