nginx通过行为识别、分层拦截与自动响应构建闭环防御机制,利用access.log字段分析异常ip,结合limit_req、map匹配、cookie校验及fail2ban实现源头实时封禁与自动运维。

Nginx 识别并阻断异常流量,关键不是等攻击发生后再补救,而是构建“行为识别 + 分层拦截 + 自动响应”的闭环机制。它不依赖外部 WAF,也能在源头快速收敛恶意请求。
从日志里挖出异常 IP 是第一步
真正有效的封禁,必须基于真实、可验证的行为证据。重点看 access.log 中这些字段:
-
$remote_addr(确保已用set_real_ip_from还原 CDN/代理后的真实 IP) -
$status(大量 403/404 往往是目录扫描) -
$request_time(极短耗时 + 高频请求,大概率是自动化工具) -
$request(含/wp-login.php、/.git/config、%2e%2e%2f等敏感路径或编码) -
$http_user_agent(python-requests、masscan、空 UA) -
$http_referer(缺失,或为http://0.0.0.0、http://127.0.0.1)
常用筛查命令:
- 1 分钟内请求超 50 次的 IP:
awk -v d="$(date -d '1 minute ago' '+%d/%b/%Y:%H:%M')" '$4 > "["d":00" && $4
- 扫描敏感路径的 IP:
awk '$7 ~ /\/wp-login\.php|\/api\/login|\/\.git\/config/ {print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -5 - 大量返回 404 且 UA 异常的组合:
awk '$9 == "404" && $12 ~ /curl|python-requests|Go-http-client/ {print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head -3
用 Nginx 原生能力实时拦截,不靠手动
日志分析是事后手段,真正的防御要前置到请求处理链路中:
- 对高危路径做精准限流(如登录接口、管理后台):
limit_req_zone $binary_remote_addr$uri zone=login_protect:10m rate=3r/m; location ~ ^/(wp-login\.php|api/login|admin/) { limit_req zone=login_protect burst=2 nodelay; return 429; # 或 return 403; } - 拦截可疑 UA 和空 Referer(配合日志审计,避免误杀):
map $http_user_agent $bad_ua { default 0; "~*masscan|nmap|sqlmap|dirbuster" 1; "~*python-requests|curl/7\.[0-9]" 1; } map $http_referer $no_referer { "" 1; "~*0\.0\.0\.0|127\.0\.0\.1" 1; default 0; } server { if ($bad_ua = 1) { return 444; } if ($no_referer = 1) { return 444; } } - 用 Cookie 值精准阻断(适用于共享 IP 场景,如代理池、CDN):
if ($cookie_PHPSESSID = "a1b2c3d4e5f6") { return 403; } if ($cookie_track_id ~ "^malicious_[0-9a-f]{8}$") { return 403; }
让封禁自动持续,而不是一次性的
手动加 iptables 或写 deny 规则容易遗漏、难维护。推荐 fail2ban:
- 编写 filter(例如匹配扫描行为):
[Definition] failregex = ^<host> -.*"(GET|POST) .*(/wp-login\.php|/\.git/config|/phpmyadmin).*" 404 ^<host> -.*"python-requests|masscan".*$ ignoreregex =</host></host> - 配置 jail(10 分钟内触发 5 次即封 1 小时):
[nginx-scan] enabled = true filter = nginx-scan logpath = /var/log/nginx/access.log maxretry = 5 findtime = 600 bantime = 3600 action = iptables[name=nginx-scan, port=http, protocol=tcp]
- 启用后用
fail2ban-client status nginx-scan查看拦截状态,IP 封禁自动生效且定时释放。
验证是否真生效,不能只看配置
- 从被识别 IP 发起请求:
curl -I http://yoursite.com/wp-login.php,应返回444(无响应)或403/429 - 实时观察日志:
tail -f /var/log/nginx/access.log | grep 192.168.1.100,确认该 IP 请求不再写入 - 检查 iptables:
iptables -L INPUT -n | grep 192.168.1.100(若用 fail2ban) - 若用 Nginx deny,需 reload:
nginx -s reload,否则不生效
不复杂但容易忽略











