CC攻击的可怕之处不在于流量有多大,而在于它精准地消耗服务器的计算资源。一个简单的数据库查询页面,正常用户访问可能只需要50毫秒,攻击者通过CC攻击让这个页面被反复请求,服务器资源瞬间被耗尽。更隐蔽的是特征型CC攻击,它模拟正常HTTP请求,每个请求的URL、User-Agent、Referer看起来都合法,传统的频率限制很难精准拦截,误伤正常用户的概率极高。要解决这个问题,必须在WAF上配置自定义规则,从请求的指纹特征入手,把攻击流量挡在业务系统之外。
理解特征型CC攻击的流量指纹特征型CC攻击和普通CC攻击最大的区别在于,攻击者会精心构造请求头,让每个请求看起来都像是来自不同的真实浏览器。但仔细分析流量日志,你会发现这些请求仍然存在共性。比如攻击工具通常使用固定的TLS指纹,也就是JA3或JA4指纹值。即使攻击者更换了IP地址、修改了User-Agent,TLS握手阶段的密码套件组合和椭圆曲线参数却很难频繁变动。另一个常见特征是HTTP Accept、Accept-Language、Accept-Encoding等头部字段的排列顺序和值完全一致,正常用户的浏览器因为版本和语言设置不同,这些字段会有自然差异。还有一种是针对特定动态页面的攻击,请求路径高度集中在某个API接口或搜索功能上,而且请求参数的长度和结构呈现规律性,比如总是携带特定长度的查询字符串,或者POST数据包的body大小固定在一个很窄的范围内。
WAF自定义规则的配置逻辑在WAF上配置自定义规则拦截特征型CC请求,核心思路不是简单地数请求次数,而是通过组合条件精准描绘攻击流量的轮廓。以ModSecurity为例,可以编写SecRule规则,结合多个变量进行判断。首先要做的是开启WAF的完整请求日志记录,包括请求头、请求体和响应状态码,收集至少一个攻击周期的流量样本。然后从样本中提取攻击流量的独特指纹。假设你发现攻击流量的Accept-Language字段值始终是"en-US,en;q=0.9",而你的正常用户主要使用中文,Accept-Language通常包含"zh-CN",这个差异就可以作为规则条件之一。再比如攻击请求的Cookie中缺少业务系统下发的特定会话标识,或者Cookie的格式不符合正常登录用户的特征,这也是一个强有力的判断维度。
基于请求头组合特征的规则编写编写WAF自定义规则时,不要只依赖单一条件,否则很容易被绕过。一个有效的规则应该同时检查多个维度。以实际场景为例,假设你运营的是一个中文内容网站,攻击者使用英文User-Agent和固定的Accept-Language发起了针对搜索接口的CC攻击。你可以在WAF中创建一条类似这样的规则:当请求路径匹配"/search"、请求方法为GET、Accept-Language值为"en-US,en;q=0.9"且User-Agent包含"Chrome/120"但请求来源IP的地理位置不属于常见英文用户地区时,直接返回403拦截或触发JavaScript挑战。这种多条件组合的规则,误伤正常英文用户的概率极低,因为正常英文用户的Accept-Language字段值会因为浏览器设置不同而存在细微差异,不会完全一致。
利用TLS指纹进行精准识别TLS指纹是目前拦截特征型CC攻击最有效的手段之一。每个HTTPS请求在建立连接时,客户端会发送Client Hello包,其中包含支持的TLS版本、密码套件列表、扩展列表和椭圆曲线参数。不同浏览器、不同操作系统、不同HTTP库生成的Client Hello包结构各不相同,这就形成了独特的JA3指纹。攻击者使用的脚本工具,比如Python的requests库或者Go语言的http客户端,其JA3指纹与真实浏览器完全不同。即使攻击者修改了User-Agent伪装成Chrome浏览器,WAF在SSL终止层面仍然可以提取到真实的JA3指纹。如果你的WAF支持JA3指纹检测,可以配置规则直接拒绝已知攻击工具的指纹。如果不支持原生JA3检测,可以通过Nginx的ssl_fingerprint模块或者HAProxy的capture功能将指纹信息传递到WAF层,然后在自定义规则中进行匹配。
# Nginx配置示例:提取JA3指纹并传递给后端WAF
ssl_fingerprint on;
location / {
proxy_set_header X-SSL-Fingerprint $ssl_fingerprint;
proxy_set_header X-SSL-JA3 $ssl_ja3;
proxy_pass http://backend;
}
# ModSecurity规则示例:基于JA3指纹拦截
SecRule REQUEST_HEADERS:X-SSL-JA3 "@pmFromFile known_attack_ja3.txt" \
"id:100001,\
phase:1,\
deny,\
status:403,\
log,\
msg:'Blocked by JA3 fingerprint matching'"
known_attack_ja3.txt文件中维护着已知攻击工具的JA3指纹列表,可以从实际攻击流量中提取并持续更新。这种基于TLS指纹的拦截方式,攻击者除非更换底层HTTP库或者修改TLS配置,否则无法绕过,而修改TLS配置对攻击者来说成本很高,会显著降低他们的攻击效率。
请求速率与行为特征的结合策略单纯的频率限制容易误伤正常用户,但将速率与行为特征结合,拦截精准度会大幅提升。具体做法是,先定义正常用户的行为基线。比如你的网站正常用户在访问文章详情页之前,通常会先访问列表页或首页,而且页面之间的访问间隔符合人类浏览节奏,一般在3到10秒之间。攻击流量则表现为直接命中目标URL,没有Referer或者Referer与目标页面没有逻辑关联,请求间隔极短且呈现机械化的均匀分布。你可以在WAF中配置一条规则:当某个IP在10秒内对同一动态页面发起了超过5次请求,并且这些请求的Referer都为空或者Referer不是本站页面,同时请求间隔的标准差小于0.5秒时,触发拦截动作。这种规则的关键在于引入了间隔标准差这个统计维度,正常用户的请求间隔是随机的,标准差较大,而脚本攻击的请求间隔高度一致,标准差极小。
针对POST型CC攻击的深度检测很多特征型CC攻击针对的是登录接口、注册接口或者密码找回功能,使用POST方法提交大量数据,目的是消耗数据库资源和短信接口费用。这类攻击的请求body通常具有固定模板,比如用户名或手机号字段的值是随机生成的,但body的整体结构和长度保持不变。WAF自定义规则可以对POST body进行深度检测。首先检查Content-Type是否为application/x-www-form-urlencoded或application/json,然后提取body的长度和特定字段的值模式。如果发现某个IP在短时间内向登录接口发送了大量POST请求,每个请求的body长度完全相同,而且username字段的值符合随机字符串的特征,比如固定长度、包含数字和字母的随机组合、不像是真实用户会使用的用户名,就可以判定为CC攻击。规则还可以进一步检查响应状态码,如果大量请求返回的都是登录失败的状态码,说明攻击者正在暴力尝试或者进行账号枚举,这同样是需要拦截的行为。
动态挑战机制的引入WAF自定义规则不一定要直接返回403拦截,更优雅的方式是引入动态挑战机制。当请求命中规则条件但置信度不是100%时,WAF可以返回302重定向,要求客户端执行JavaScript跳转或者携带正确的Cookie才能继续访问。这种方式对正常用户几乎无感,因为现代浏览器会自动处理重定向和Cookie,而攻击脚本通常没有JavaScript执行能力,或者攻击者为了效率不会去解析和执行JS代码。你可以在WAF规则中设置,当请求的TLS指纹不在已知浏览器指纹白名单中,并且请求速率超过阈值时,触发JS挑战。挑战成功后的客户端会被标记一个临时白名单Cookie,后续请求在Cookie有效期内直接放行。这样既拦截了自动化攻击,又不影响搜索引擎爬虫和正常用户。
规则优化与误伤处理任何WAF规则上线后都需要持续监控和优化。误伤是不可避免的,关键是要有快速发现和处理误伤的机制。建议在规则中先使用"log"动作而不是"deny",观察一段时间确认没有误伤后再切换为拦截模式。同时配置WAF的告警通知,当某条规则的触发量突然飙升或者某个正常用户的请求被拦截时,能够及时收到通知。对于被拦截的请求,返回的页面应该包含清晰的提示信息和联系方式,方便真实用户反馈问题。另外要定期分析WAF日志,检查是否有新的攻击特征出现,及时更新规则条件。攻击者也在不断进化,他们可能会更换TLS指纹、修改请求头组合、调整请求速率来绕过检测,你的规则也需要跟着迭代。建立一个攻击特征库,把每次发现的攻击指纹记录下来,形成持续积累的防御知识体系,这样WAF的拦截能力会越来越强,误伤率越来越低。
配置WAF自定义规则拦截特征型CC请求,本质上是与攻击者进行持续的对抗博弈。攻击者寻找的是通用漏洞和低成本的攻击方式,而你通过深入分析流量特征、组合多个维度的判断条件、引入TLS指纹和动态挑战机制,可以大幅提高攻击者的成本,迫使他们放弃攻击或者转向其他更容易的目标。这些规则配置完成后,WAF就从一个简单的请求过滤器,升级成了具备智能识别能力的业务安全网关。
