3. 登录Velocity的度量

登录Velocity的度量 学习卡片

在识别暴力破解与撞库攻击时,单看“失败次数”远远不够。攻击者可以用极慢的速度绕过阈值,也可以在短时间内爆发式尝试。要准确刻画登录行为的“速度感”,我们需要引入一组可计算的Velocity指标:时间窗口、频率、速率、加速度。它们共同回答一个问题——单位时间内,登录尝试的密度和变化趋势是否异常。

一、核心原理:从计数到速度的四个层次

Velocity度量的本质是把离散的登录事件映射到时间轴上,再提取统计特征。四个指标层层递进:

  • 时间窗口(Time Window):定义观察的时间范围,如60秒、5分钟、1小时。窗口决定了“多快算快”的基准。
  • 频率(Frequency):窗口内事件发生的总次数,是最基础的计数指标。
  • 速率(Rate):频率除以窗口时长,得到单位时间的事件密度,如“每分钟失败次数”。
  • 加速度(Acceleration):速率的变化率,即当前速率相对于上一窗口速率的增量,用于捕捉“突然加速”的攻击行为。

关键公式:

频率 F = 窗口内事件数 N

速率 R = N / T(T为窗口时长,单位秒或分钟)

加速度 A = (R当前 - R上一窗口) / T窗口间隔

这四个指标不是孤立的。时间窗口是分母,频率是分子,速率是归一化结果,加速度则引入了时间序列的对比。只有组合使用,才能同时覆盖“低频慢速”和“高频爆发”两类攻击。

二、方法流程:滑动窗口与指标计算

实际工程中,最常用的计算方式是滑动时间窗口。与固定窗口(如每5分钟清零)相比,滑动窗口能避免“窗口边界攻击”——攻击者在窗口切换瞬间集中尝试。流程如下:

  1. 事件采集:从登录日志中提取每条记录,字段至少包含:时间戳、用户名、源IP、登录结果(成功/失败)。
  2. 窗口定义:选择窗口长度T和滑动步长S。例如T=60秒,S=10秒,表示每10秒计算一次过去60秒的指标。
  3. 分组维度:按源IP、用户名或IP+用户名组合分组,分别计算。不同维度对应不同攻击场景。
  4. 频率统计:对每个分组,统计窗口内失败登录次数N。
  5. 速率计算:R = N / T。若T=60秒,N=30,则R=0.5次/秒,即30次/分钟。
  6. 加速度计算:保存上一窗口的速率Rprev,当前加速度A = (R - Rprev) / S。
  7. 阈值判定:根据业务基线设定阈值,如R>10次/分钟或A>5次/分钟²触发告警。

下面是一个简化的Python伪代码,展示滑动窗口内频率、速率和加速度的计算逻辑:

from collections import deque
import time

class VelocityTracker:
    def __init__(self, window_sec=60, step_sec=10):
        self.window = window_sec
        self.step = step_sec
        self.events = deque()          # 存储(时间戳, 结果)
        self.prev_rate = 0.0
        self.last_calc = time.time()

    def add_event(self, ts, success):
        self.events.append((ts, success))
        self._evict(ts)

    def _evict(self, now):
        while self.events and now - self.events[0][0] > self.window:
            self.events.popleft()

    def compute(self, now):
        if now - self.last_calc < self.step:
            return None
        self._evict(now)
        fail_count = sum(1 for _, s in self.events if not s)
        rate = fail_count / self.window          # 次/秒
        accel = (rate - self.prev_rate) / self.step
        self.prev_rate = rate
        self.last_calc = now
        return {"frequency": fail_count, "rate": rate, "acceleration": accel}

该实现中,window_sec和step_sec是核心参数。窗口越长,越能捕捉慢速攻击;步长越短,加速度越灵敏,但计算开销也越大。

三、案例场景:三种攻击的Velocity特征

假设我们监控某电商登录接口,窗口T=60秒,步长S=10秒。以下三种场景的指标表现截然不同:

场景 频率(次/分) 速率(次/秒) 加速度(次/秒²) 特征解读
正常用户 1~3 0.02~0.05 接近0 低频稳定,无突变
慢速撞库 5~8 0.08~0.13 0.001~0.003 频率略高但加速度平缓,易漏报
暴力破解爆发 120~300 2~5 0.1~0.5 频率和加速度同时飙升,易触发告警

从表中可见,慢速撞库的频率可能只比正常用户高2~3倍,但加速度极低,单纯依赖频率阈值容易漏报。此时应结合长时间窗口(如1小时)的累计频率,或引入用户名分散度辅助判断。而暴力破解的加速度特征非常明显,适合用短窗口+加速度阈值快速响应。

实战建议:对同一IP,同时维护短窗口(60秒)和长窗口(3600秒)两套指标。短窗口抓爆发,长窗口抓慢速。两者任一超阈值即告警,可显著降低漏报和误报。

四、误区与复盘:计算Velocity时容易踩的坑

即使公式正确,工程实现中仍有几个常见误区:

  • 误区一:固定窗口代替滑动窗口。固定窗口在边界处会丢失连续性,攻击者只需在窗口切换时暂停几秒即可绕过。务必使用滑动窗口或至少重叠窗口。
  • 误区二:忽略成功登录。撞库攻击中,攻击者可能用少量成功登录掩盖大量失败。Velocity应同时统计失败和成功,或计算“失败/成功比”的速率。
  • 误区三:加速度分母错误。加速度是速率的变化除以时间间隔,不是除以窗口长度。若用窗口长度做分母,会严重低估加速度。
  • 误区四:所有用户共用阈值。不同业务线的正常登录频率差异巨大。应按业务线或用户角色分别建立基线,再设定动态阈值。
  • 误区五:只按IP聚合。攻击者使用代理池时,单IP频率不高,但按“用户名”或“IP段”聚合后速率会暴露。多维度聚合是必要的。

复盘要点:每次告警后,回看该时间段的频率、速率、加速度曲线。如果加速度在告警前已经持续上升,说明阈值可以提前;如果加速度始终平缓但频率缓慢累积,说明需要补充长窗口指标。Velocity度量不是一次配置就一劳永逸,而是需要根据误报和漏报持续调参。

总结来说,时间窗口定义了观察尺度,频率提供了绝对数量,速率实现了跨窗口可比,加速度捕捉了趋势突变。四者结合,才能把“登录太快”这个模糊感觉,转化为可计算、可告警、可复盘的工程指标。