CC攻击的可怕之处不在于带宽耗尽,而在于它用极低的成本模拟了真实用户的高频访问。在流量清洗的对抗中,一个核心难题始终横亘在防护者面前:如何区分一个突发的高并发正常用户和一个恶意的CC攻击脚本?答案往往不在流量的总量里,而在时间的缝隙中。请求间隔分布,就是那把丈量时间缝隙的尺子。
正常的用户访问,无论是人类点击还是App轮询,其请求间隔通常遵循某种概率分布,比如泊松分布或带有长尾特征的幂律分布。人类在浏览网页时,会有阅读、思考、滚动页面的停顿,这些停顿构成了请求之间的自然间隙。而绝大多数低级的CC攻击脚本,为了追求攻击效率,往往采用固定间隔或极窄范围内的随机间隔发送请求。这种机械化的节奏,在统计学上会形成一种病态的“规律感”,与真实流量的混沌感形成鲜明对比。
因此,在CC防护的底层逻辑里,我们不再仅仅盯着每秒查询率(QPS)的绝对值,而是开始剖析流量的时序特征。一个QPS为1000的源IP,如果请求间隔的变异系数极低,其威胁程度远高于一个QPS为2000但间隔分布符合人类行为模型的源IP。这就是基于请求间隔分布进行异常突发检测的出发点。
从时间戳到间隔序列的构建要在高并发网关上进行实时检测,第一步是数据预处理。我们不能直接分析原始的纳秒级时间戳,而是需要构建一个滑动窗口内的间隔序列。假设某个源IP在时间窗口T内发起了N次请求,其时间戳分别为 t1, t2, t3 ... tn。我们计算相邻请求的差值:Δt_i = t_i - t_{i-1}。这个序列 [Δt_1, Δt_2, ...] 就是该IP的行为指纹。
在实际工程中,为了消除网络抖动带来的噪音,通常会对间隔序列进行一阶低通滤波或中值滤波。例如,排除那些因为TCP重传导致的极短间隔(微秒级),这些往往是底层协议栈的行为,而非攻击者的本意。处理后的间隔序列,才能真实反映攻击工具的发包逻辑。
核心检测指标:变异系数与熵面对清洗设备,攻击者会尝试伪装,比如使用随机函数来生成间隔。但伪随机数的随机性往往很差。这时候,我们需要引入两个硬核指标:变异系数(CV)和信息熵。
变异系数是标准差与平均值的比值。对于真实用户,由于存在偶然的长停顿(比如接电话),其标准差的波动极大,CV值通常很高且不稳定。而对于CC攻击脚本,即使它把平均间隔拉长到和人类一样,只要它是基于固定算法生成的,其CV值往往会收敛在一个异常狭窄的区间内。我们可以设定动态阈值,当CV值持续低于0.3时,触发行为可疑告警。
比CV更深入的是间隔序列的信息熵。我们将间隔时间按对数尺度分桶,计算每个桶内间隔出现频率的概率分布,进而求香农熵。真实流量的熵值通常较高,因为行为多变;而脚本流量的熵值偏低,因为状态机有限。这种基于熵的检测方法,对那种“慢速CC攻击”尤为有效——攻击者自以为放慢速度就能隐身,却不知其单调的节奏暴露了低熵的本质。
突发检测的时域与频域分析单纯的间隔分布能识别出“匀速”或“伪随机”的机器人,但还有一种更狡猾的攻击模式:脉冲式突发。攻击者平时静默,每隔几分钟突然爆发一次高密度请求,试图通过平均值拉低峰值特征来绕过滑动窗口的QPS限制。
针对这种异常突发,我们需要在时域上进行波峰检测。一种高效的工程化算法是改进的Z-Score峰值检测。我们维护一个基于过去30分钟间隔数据的滚动均值和滚动标准差,当某个新的间隔数据点(或短时突发密度)偏离均值超过3.5个标准差时,标记为异常突发。为了防止算法被攻击者“训练”污染,必须建立白模型保护机制,即只将确认无误的正常流量纳入滚动统计。
更进一步,我们可以将请求序列视为离散信号,进行快速傅里叶变换(FFT)。正常的网页浏览没有明显的频率特征,频谱图是杂乱无章的。而带有定时器轮询的CC脚本,在频域上会表现出明显的能量尖峰。哪怕这个尖峰对应的频率很低(例如每10秒一次),在频谱图上也会一目了然。通过监测频域上的异常能量聚集,可以捕捉到那些在时域上隐藏得很好的周期性攻击。
基于马尔可夫链的状态转移检测高阶的攻击模拟会尝试模仿人类的浏览链路,但在请求间隔上往往露出马脚。人类的请求间隔具有记忆性,上一个动作的停顿会影响下一个动作。我们可以将请求间隔离散化为几个状态:短间隔(<100ms)、中间隔(100ms-1s)、长间隔(1s-10s)、超长间隔(>10s)。然后统计这些状态之间的转移概率矩阵。
对于真实用户,状态转移是平滑且具有上下文关联的。例如,从“短间隔”直接跳到“超长间隔”的概率极低,通常需要经过“中间隔”过渡。而很多攻击脚本,由于代码逻辑是“发送请求-随机睡眠-发送请求”,其状态转移是独立且生硬的,会频繁出现从“短间隔”直接突变到“长间隔”再突变回来的情况。通过计算状态转移矩阵与正常基线的KL散度,可以有效识别出这种逻辑生硬的机械流量。
工程落地中的哈希分桶与内存优化在10Gbps以上的骨干网流量清洗场景,不可能为每一个源IP建立复杂的统计模型。这就需要在内存和精度之间做折中。通常采用Count-Min Sketch或基于哈希的漏斗结构。我们只对命中频次超过一定阈值的活跃IP建立详细的行为画像。
对于间隔数据的存储,不需要存储全量时间戳,只需要存储指数加权移动平均值(EWMA)和近期的平方差累积。算法伪代码如下:
# 针对单IP的轻量级间隔检测器
class IntervalDetector:
def __init__(self, alpha=0.05, threshold=3.0):
self.ewma = None
self.ewmvar = None
self.alpha = alpha
self.threshold = threshold
def update(self, interval):
if self.ewma is None:
self.ewma = interval
self.ewmvar = 0
else:
delta = interval - self.ewma
# 更新均值和方差估计
self.ewma = self.ewma + self.alpha * delta
self.ewmvar = (1 - self.alpha) * (self.ewmvar + self.alpha * delta2)
def check_anomaly(self, interval):
if self.ewma is None: return False
# 计算当前偏差程度
std = math.sqrt(self.ewmvar)
if std < 1e-6: return False
z_score = abs(interval - self.ewma) / std
return z_score > self.threshold
这段逻辑极其轻量,每个IP只占用几十个字节的内存,却能在流量清洗层实时计算请求间隔的异常偏离程度。当检测到异常突发时,结合IP的信誉库和指纹特征,决定是弹出JS挑战、验证码还是直接丢弃。
对抗模拟器与高级逃逸的博弈攻击者也在进化。现在的CC攻击工具开始集成浏览器自动化框架,甚至能执行JS并维持Cookie会话。面对这种“全模拟”攻击,简单的间隔分布检测可能会失效,因为工具在模拟人类操作时,会引入真实的阅读停顿。
此时,我们需要将检测维度从“请求间隔”升级到“交互节奏”。即使是自动化脚本,其底层WebDriver的执行周期与真实人类的生物节律仍有细微差别。人类使用鼠标或触摸屏产生的请求,其时间戳带有微秒级的肌肉震颤噪声;而脚本生成的请求,时间戳往往是系统定时器的整数倍,缺乏这种底噪。通过分析时间戳最后几位比特的均匀分布性,可以识别出这种深层次的自动化特征。这种基于硬件时钟漂移与软件定时器差异的检测方法,是目前对抗高级CC攻击的前沿手段。
请求间隔分布与异常突发检测,本质上是一场在时间维度上的军备竞赛。从简单的频率限制,到统计分布的拟合,再到硬件时钟指纹的比对,防护策略的颗粒度越细,攻击者的绕过成本就越高。在流量清洗的战场上,谁能在毫秒级的时间碎片中捕捉到异常,谁就能守住业务的安全底线。
