识别并防御恶意扫描器需在连接初期捕获指纹:tls握手阶段拒接无sni或非法sni连接,监控error.log中文件未找到日志触发fail2ban封禁,nginx配置层禁止隐藏文件、敏感后缀访问及恶意ua/路径,辅以混淆策略提高扫描成本。

识别并防御恶意扫描器,关键在于抓住它在连接建立初期就暴露的“指纹”,而不是等它发完请求再反应。Nginx 天然具备在 TLS 握手、HTTP 请求头、文件系统访问等多个环节捕获异常行为的能力,只需合理配置,就能把多数自动化扫描挡在门外。
从 TLS 握手阶段揪出扫描器
真实浏览器或合规客户端在发起 HTTPS 连接时,会携带合法 SNI、使用现代协议版本、选择标准密钥套件;而 masscan、sslscan、nmap 等工具往往暴露明显特征:
- 启用 ssl_reject_handshake on,让无 SNI 或 SNI 不匹配任何
server_name的连接在 ClientHello 后立即断开(返回 UNRECOGNIZED_NAME) - 禁用通配符域名(如
*.example.com)和default_server,否则会绕过 SNI 检查 - 在日志中记录
$ssl_server_name、$ssl_protocol、$ssl_cipher,筛查空值、IP 地址、test.local 类随机域名,以及 SSLv3/TLSv1/RC4-SHA 等老旧组合 - 设置 ssl_handshake_timeout 3s(Nginx 1.19+),阻断 Slow-SSL 类慢速握手攻击
盯紧 error.log 里的“找不到文件”痕迹
全路径暴力扫描(如 /.env、/wp-admin/install.php、/backup.zip)不会总触发 404 页面,但只要 Nginx 尝试打开一个不存在的磁盘路径,就会稳稳记入 error.log:
- 典型日志条目:
[error] ... open() "/www/site/public/.env" failed (2: No such file or directory), client: 192.168.32.11, request: "GET /.env HTTP/1.1" - 该日志不受 rewrite、内部重定向干扰,比 access.log 更可靠
- 用 fail2ban 监控 error.log,正则匹配
open\(\) ".*?(\.git|\.env|phpmyadmin|wp-admin|backup|install).*?failed \(2: No such file or directory\),3 分钟内命中 5 次即封 IP 1 小时
在 Nginx 配置层提前拦截高危请求
别等日志报警才行动,把防御逻辑下沉到 server 块,实现毫秒级响应:
- 禁止点开头的隐藏文件:
location ~ /\. { return 404; } - 直接断连敏感后缀下载:
location ~ \.(zip|rar|sql|bak|gz|7z|env|log|conf)$ { return 444; } - 用 map 指令动态标记恶意 UA 或路径:
map "$http_user_agent:$request_uri" $is_scan { default 0; "~*python-requests.*\/\.env" 1; "~*zgrab.*\/wp-admin" 1; },再在 location 中if ($is_scan) { return 403; } - 收紧连接生命周期:
client_header_timeout 7s;(防 Slowloris)、keepalive_timeout 15s;、limit_conn_zone $binary_remote_addr zone=scan:10m;+limit_conn scan 2;
补充策略:混淆与反制
让扫描器“误判成本”升高,是降低其持续动力的有效方式:
- 对常见扫描后缀(.zip、.sql、.bak)做重定向到大文件(如 10GB 测速包),拖慢其扫描节奏
- 对高频试探 IP,在拦截前插入轻微延迟(如
return 429;配合limit_req),避免被快速识别为防火墙 - 清理服务器上残留的备份文件、测试页面、未删的 .git 目录——没有可探测目标,扫描自然停止











