
一、为什么空 UA 可疑
User-Agent 是 HTTP 协议的请求头之一,浏览器、搜索引擎爬虫、正规 SDK 都会正确设置。而很多攻击脚本、扫描器为了省事,直接不设置 UA,或者用最简化的默认值。于是”空 UA”就成了一个相当有效的异常信号。
统计经验值:正常网站访问中,空 UA 请求占比通常低于 1%;如果你的站点空 UA 占比超过 5%,基本可以确定有自动化程序在扫。
二、WAF 的识别维度
| 检测项 | 判定逻辑 |
|---|---|
| UA 是否为空 | 请求头不存在 UA 或值为空字符串 |
| UA 是否过短 | 长度小于 10 个字符 |
| UA 是否命中工具库 | python-requests、curl、wget、Go-http-client 等 |
| UA 与指纹是否一致 | UA 声称 Chrome 但 JA3 指向脚本工具 |
| UA 与行为是否匹配 | 声称搜索引擎爬虫,但请求路径不含 sitemap |
这几项里,“UA 与 JA3 交叉验证”是最可靠的——攻击者可以随便改 UA 文本,但改不了底层 TLS 指纹。
三、直接拦截的误伤风险
空 UA 不等于必然是攻击,这些情况也会出现空 UA:
- 物联网设备、智能硬件的内置 HTTP 客户端
- 部分老旧浏览器或精简版浏览器
- 第三方系统对接你的 API(未规范设置 UA)
- 健康检查探针、监控系统
- 部分 RSS 阅读器、下载工具
如果你的站点有 API 对外服务,直接封空 UA 可能把正常调用方一起封掉。
四、推荐的配置策略
4.1 分级处置
WAF 策略建议:
├─ 空 UA + 访问高频接口 → 拦截
├─ 空 UA + 访问敏感路径 → 拦截
├─ 空 UA + 正常页面 → JS 验证 / 观察
├─ 已知工具 UA → 拦截或限速
└─ API 路径 → 加白名单,不检测 UA
4.2 API 路径单独放行
对 /api/ 开头的路径设置例外,改用 Token 鉴权而不是 UA 检测。这样既不影响对接方,也不留风险口子。
4.3 先观察后拦截
开启前先用”观察模式”跑一周,导出空 UA 的 IP 及访问路径清单,人工确认没有业务流量被误判,再切拦截。
五、Nginx 侧实现
server {
# 空 UA 或超短 UA 直接拒绝
if ($http_user_agent ~ "^$") {
return 403;
}
if ($http_user_agent ~ "^.{0,5}$") {
return 403;
}
# 常见工具 UA 黑名单
if ($http_user_agent ~* "(python-requests|curl|wget|Go-http-client|scrapy|java)") {
return 403;
}
# API 路径不做 UA 检测
location /api/ {
# 单独鉴权逻辑
proxy_pass http://127.0.0.1:8080;
}
}
宝塔路径:网站 → 设置 → 配置文件。注意 `if` 语句在 Nginx 中有性能开销,建议放在 server 块顶层,不要放在 location 内部。
六、更好的替代思路
与其纠结 UA,不如把重心放到行为特征上:
- 看访问路径是否符合真人浏览规律(是否会加载 CSS/JS 等静态资源)
- 看是否携带完整的请求头(Accept、Accept-Language、Referer)
- 看 TLS 指纹是否与声明的一致
- 看请求间隔是否机械规律(真人有停顿,脚本间隔均匀)
多维交叉验证比单看 UA 准确得多,误伤也小得多。
七、小结
空 UA 是一个有价值的异常信号,但不建议”一刀切”式硬拦截。合理做法是:空 UA + 异常行为 = 拦截,空 UA + 正常访问 = 验证或观察。配合 JA3 指纹交叉验证,识别准确率会高得多。
