拿到服务器报警短信的时候,我第一时间拉取了那段时间的Nginx访问日志。流量图上显示,在凌晨三点到四点之间,突然涌入了一波诡异的请求高峰,峰值连接数直接顶满了带宽上限。这不是那种瞬间打死的脉冲式DDoS,而是一种慢刀子割肉的CC攻击。攻击者显然在刻意模拟真实用户的访问轨迹,但凡是模拟,就一定有破绽。我把日志导出来,逐行分析,很快发现这批请求全都集中在几个高消耗的动态接口上,比如搜索接口和复杂的报表生成页面。
最先引起我注意的不是请求频率,而是请求头的极度不整齐。正常的浏览器访问,哪怕是经过伪装的爬虫,User-Agent的分布也会呈现某种规律,比如集中在几款主流浏览器内核上。但这批日志里,User-Agent五花八门,从老掉牙的IE6到最新版的Edge,从Windows到macOS甚至还有各种Linux发行版的浏览器标识。这种刻意制造的多样性,恰恰暴露了代理池的本质。真实的用户群体不可能在凌晨时段同时出现如此跨越十年的设备代际差。
请求头的指纹级异常进一步深挖请求头,我发现了更致命的特征。正常的浏览器发起的请求,Header中携带的Accept、Accept-Language、Accept-Encoding等字段通常是强关联的。比如中文版Chrome浏览器,Accept-Language大概率是zh-CN,zh;q=0.9,而且会明确声明支持gzip和deflate压缩。但这批恶意请求中,大量出现了Accept-Language为en-US但其他头部字段却是中文浏览器的配置,甚至有些请求完全缺失了Accept-Encoding字段。这种字段间的逻辑撕裂感,说明这些请求是由不同编程语言编写的脚本工具发出的,而非完整的浏览器环境。
还有一个更隐蔽的细节,是关于HTTP协议版本的声明。在这批攻击日志中,混杂着大量HTTP/1.1的请求,但它们的Connection字段处理得很粗糙。现代浏览器和HTTP库在处理长连接时,会自动处理Keep-Alive的细节,但这里很多请求要么强行在HTTP/1.1下关闭连接,要么在应该携带Keep-Alive超时参数时完全忽略。这种协议栈实现的差异,像指纹一样把这些代理节点背后的真实运行环境暴露了出来。
IP池的时空分布与活跃度分析把攻击时段的所有来源IP提取出来后,我做了简单的统计分析。这批IP总数大约有三千多个,乍看之下分布在全球各地,似乎是一个高质量的分布式代理池。但当我用IP归属地数据库去解析时,发现了一个违背常识的现象:这些IP的运营商归属高度集中,大量IP落在了几个特定的廉价云服务商和主机托管商的AS号段内。真实的全球用户访问,运营商分布应该是与当地电信基础设施比例相符的,而这种集中在特定低成本机房的分布模式,是代理池搭建者为控制成本而批量采购VPS的典型特征。
接着我分析了这些IP的历史活跃度。通过查询内部的安全日志库,我发现其中超过七成的IP在攻击发生前的24小时内,没有任何访问本站的记录。这说明攻击者使用的是一次性或者低频轮换的代理资源。更有意思的是,这些IP在发起CC攻击的同时,几乎没有任何一个IP去请求网站根目录下的静态资源,比如CSS、JS或者图片文件。正常用户访问网页,浏览器会自动解析HTML并触发后续的静态资源请求,这是浏览器的本能行为。但这些攻击请求只盯着目标动态接口,发完就走,完全没有任何后续的资源加载行为。这种“只打点不扫面”的访问模式,是区分代理池攻击和真实爬虫的关键指标。
时序行为与请求间隔的机械感真实用户的访问间隔是符合泊松分布的,有时快有时慢,存在明显的人为思考和操作停顿。但这次CC攻击的日志里,我看到了大量精确到毫秒级的固定间隔请求。我把某个单一IP的请求时间戳提取出来,计算相邻请求的时间差,发现存在大量间隔恰好为1秒、0.5秒甚至0.1秒的连续请求。这种机械化的节奏感,要么是脚本里写了固定的sleep函数,要么是压测工具的参数配置。人类的手指绝对不可能在键盘或鼠标上敲出如此精准的节拍。
更狡猾的攻击者会引入随机延迟来掩盖这种机械感,但随机数的生成质量往往又会露出马脚。我分析了几个看似随机间隔的IP,发现它们的时间差分布过于均匀,在设定的最小值和最大值之间几乎是平坦分布的。而真实用户的停顿时间通常集中在某些特定值附近,比如阅读一段文字需要几秒钟,填写表单需要更久。攻击脚本生成的均匀随机延迟,在统计学上反而显得不自然。
JA3指纹与TLS握手的隐秘线索在分析HTTPS请求时,JA3指纹给了我决定性的判断依据。JA3是对TLS握手过程中Client Hello包的加密套件、扩展列表、椭圆曲线和椭圆曲线格式等字段进行MD5哈希后生成的指纹。同一个浏览器或同一个编程语言的TLS库,发出的JA3指纹通常是固定的。我提取了攻击时段所有请求的JA3指纹,结果发现,虽然User-Agent千变万化,但JA3指纹却高度集中在少数几个值上。
其中出现频率最高的一个JA3指纹,经过查询,对应的是某个版本的Python Requests库的默认TLS配置。另一个高频指纹则指向了Go语言标准库的HTTP客户端。这就很清楚了,攻击者虽然给每个请求换了不同的User-Agent字符串,但底层发请求的工具始终是那几款脚本语言。这种上层伪装与底层指纹的割裂,是识别代理池攻击最硬核的证据。因为真实浏览器的TLS实现远比脚本库复杂,而且不同版本、不同操作系统的浏览器,其JA3指纹是各不相同的,绝不可能出现几百个不同User-Agent却共享同一个JA3指纹的情况。
代理节点的HTTP代理头特征很多代理节点在转发请求时,会在HTTP头里留下痕迹。我在这批攻击日志中发现了大量携带X-Forwarded-For、X-Real-IP、Via等代理相关头部的请求。这些头部本意是让后端服务器知道原始客户端的真实IP,但在攻击场景中,它们反而成了识别代理节点的标签。有些配置粗糙的代理服务器,甚至会直接把客户端的真实内网IP暴露在X-Forwarded-For字段里,比如192.168.x.x或者10.x.x.x这样的私有地址。这说明攻击者的代理链上游,还存在一些配置不当的跳板节点。
还有一个值得注意的头部是Proxy-Connection。这是某些老式HTTP代理服务器特有的字段,现代浏览器早已不再使用。当我在日志中看到这个字段时,几乎可以断定该请求经过了HTTP正向代理,而非来自直接连接。把这些携带代理痕迹的请求IP汇总后,我发现它们构成了攻击流量中一个明显的子集,这部分代理节点的质量相对较低,更容易被识别和封禁。
攻击目标的路径选择策略分析攻击请求的URL路径,能反推出攻击者的意图和情报来源。这次CC攻击主要针对的是三个接口:商品搜索接口、动态定价计算接口,以及一个带有复杂数据库联表查询的用户订单列表页面。这三个接口的共同特点是,都需要消耗大量的后端计算资源和数据库查询,任何一个请求的处理时间都在200毫秒以上。攻击者显然事先做过功课,知道哪些页面是性能瓶颈。
但他们的路径选择也暴露了情报的滞后性。其中一个被重点攻击的接口,实际上我们在两周前已经做了重构,增加了缓存层,性能提升了五倍以上。攻击者仍然按照旧的思路在打这个接口,说明他们手中的目标列表没有及时更新。这种情报更新的延迟,是防御方可以利用的时间窗口。通过定期对高消耗接口进行重构或增加缓存策略,可以让攻击者耗费资源去打一个已经变得很轻量的目标,从而降低攻击的效费比。
防御策略的精准构建基于以上分析,我们可以构建一套针对恶意代理池的精准防御体系。首先,针对JA3指纹,可以在反向代理层或者WAF上配置JA3指纹的黑名单或白名单策略。对于来自Python、Go等脚本语言默认TLS指纹的请求,如果是访问高消耗的动态接口,可以直接拦截或者施加更强的验证挑战。这种方法对代理池攻击非常有效,因为攻击者很难在不修改底层库的情况下改变JA3指纹。
其次,针对请求头的逻辑一致性,可以编写检测规则。当发现User-Agent与Accept-Language、Accept-Encoding等头部存在明显矛盾时,或者缺失关键头部字段时,可以标记为可疑请求并触发验证码。这种规则不需要依赖IP信誉库,完全基于请求本身的特征,对新型代理IP同样有效。
再者,针对访问行为的时序分析,可以设置动态的速率限制。与传统的固定阈值限流不同,动态速率限制会分析请求间隔的分布模式。如果检测到过于均匀或者过于机械的请求节奏,即使绝对频率不高,也可以主动介入。这种基于行为模式的限流,比简单的计数器限流更难被绕过。
最后,对于那些携带代理头部的请求,可以采取更严格的信任策略。正常的CDN或反向代理通常有固定的IP白名单,而来自非白名单IP却携带X-Forwarded-For等头部的请求,大概率是恶意代理,可以直接拒绝服务。
日志分析脚本的思路分享在实际操作中,我写了一个简单的分析脚本来快速提取这些特征。核心逻辑并不复杂,主要是对日志字段的聚合统计和异常检测。下面是一段简化后的分析逻辑,供参考:
# 提取请求间隔的分布特征
cat access.log | awk '{print $1, $4}' | sort | uniq -c | \
awk '{
# 计算相邻请求的时间差
if(prev_ip == $2) {
diff = timestamp - prev_time
print diff
}
prev_ip = $2; prev_time = timestamp
}' | sort -n | uniq -c | sort -rn | head -20
这段代码的思路是,先把每个IP的请求时间戳提取出来,然后计算同一IP相邻请求之间的时间间隔,最后统计这些间隔的分布情况。如果发现大量集中在某个固定值附近的间隔,那就很有可能是脚本攻击。
对于JA3指纹的提取,需要借助更专业的工具,比如通过Nginx模块或者旁路流量分析来获取。但一旦建立起JA3指纹库,就能在攻击发生的早期快速识别出代理池的底层工具链,从而做出针对性的拦截决策。这些特征规律不是一成不变的,攻击者也在不断进化他们的伪装技术,但底层协议栈和网络行为的物理规律,远比上层User-Agent字符串更难伪造。抓住这些不变的底层特征,就是在攻防对抗中占据主动的关键。
