用信息熵与随机森林算法对抗爬虫实践

前言:看不见的对手

11-28更新:浏览新闻发现 WhatsApp 存在同样的安全问题,导致 35 亿个账户数据泄露。信息来自 SBA 文章:《Researchers discover security vulnerability in WhatsApp》。

在做应用安全防御时,我们经常会遇到一种棘手的情况:对手不再是傻乎乎的脚本小子,而是经过精心伪装的高级爬虫。它们的 IP 是干净的,设备指纹看起来也是正常的,甚至请求频率都控制在人类操作的范围内。面对这种“隐形”的流量,传统的封 IP 或封设备 ID 往往显得力不从心——要么封不住,要么误伤一片。

今天我想分享一个真实的对抗案例,聊聊在常规手段失效后,我是如何利用 信息熵(Information Entropy) 结合 随机森林(Random Forest) 算法,从数据规律的层面揪出这批“内鬼”的。

场景复现:通讯录里的猫腻

先交代一下背景。本次被攻击的接口是一个典型的“手机通讯录上传/匹配”场景。客户端会上传一个包含若干手机号的 phones 数组,后端负责返回这些号码对应的用户信息。

请求报文大概长这样:

POST /api/search_phones HTTP/1.1
Host: www.example.com

{
  "phones": [
    "13800138001",
    "13800138002",
    "13800138003",
    "13800138004",
    "13800138005",
    "...",
    "13800138010"
  ]
}

作为安全工程师,当你盯着这段日志看时,直觉会告诉你哪里不对劲。 没错,是“秩序”。

正常用户的通讯录,号码通常是杂乱无章的(你的朋友不可能恰好都连号)。而爬虫为了遍历数据,往往会生成连续、递增或者具备某种数学规律的号码列表。

这就引出了我们今天的核心解题思路:既然坏人的特征是“有规律”,那我们将“规律性”转化为计算机可理解的数值,问题不就解决了吗?

特征提取:用数学量化“混乱”

如何判断一组数字是“有规律”还是“乱序”?简单的做法是统计前缀重复率,比如“如果前7位相同的号码超过50%,就判黑”。但这太容易被绕过了,攻击者稍微打乱一下顺序就能骗过规则。

为了追求更强的鲁棒性,我们需要引入信息论中的经典概念:熵(Entropy)

📝 知识点:信息熵 源于香农信息论,用于量化系统的“混乱程度”或“不确定性”。

  • 熵值越高:系统越混乱,越不可预测(正常用户特征)。
  • 熵值越低:系统越有序,越有规律(爬虫特征)。

数学公式如下:

H(X)=ipilog2piH(X) = -\sum_i p_i \log_2 p_i

1. 第一层过滤:号码分布信息熵

首先,我们可以计算这组号码分布的熵值。当一个存在规律的号码数组输入到公式中时,其计算出的熵值会明显低于随机号码。

为了验证这个思路,我选取了100个号码作为样本,模拟了不同程度的“前缀聚集”情况,计算结果如下表:

相同前缀占比 : 随机占比相同前缀数量随机前缀数量前缀总种类数计算出的熵值 (bit)
100% : 0%100010.00 (完全有序)
90% : 10%9010110.80
50% : 50%5050513.82
10% : 90%1090916.31
0% : 100%01001006.64 (完全随机)

结论显而易见: 熵值越低,恶意攻击的嫌疑越大。

2. 进阶特征:差值(步长)信息熵

虽然上面的方法有效,但狡猾的爬虫可能会对生成的连续号码进行随机打乱(Shuffle)。这时候,单纯看号码分布可能就不够准了。

于是我引入了第二个核心特征:号码间差值的信息熵

逻辑是这样的: 先将数组内的号码进行排序,然后计算相邻号码之间的差值(步长),最后计算这些差值的熵。

这样一来,无论爬虫怎么打乱顺序,只要它生成的号码本质上是基于某种步长(比如 +1, +3)覆盖的,排序后的差值就会呈现出极高的重复性,从而导致差值熵极低

这不仅能识别连号,还能覆盖各种等差数列变种:

  • ...001, ...002, ...003 (步长为1)
  • ...001, ...004, ...007 (步长为3)
  • 1234... , 1235... , 1236... (中间号段递增)

经过实测,号码间差值信息熵在描述号码组的“相似性”上,比单纯的号码分布熵更加敏锐。

3. 构建全维度的特征工程

为了不给对手留死角,我并没有只依赖一个指标,而是组合了一套“特征全家桶”。基于前面的思路,我们可以提取出以下 8 个关键维度:

序号指标名称爬虫样本典型值正常样本典型值业务含义
1前缀相同比例 Top N 和0.790.05头部最集中的几个前缀占比之和
2前缀频率最高值0.500.02出现次数最多的前缀占比
3前缀熵归一化值0.340.95核心指标:值越小越可疑
4差值相等比例最高值0.710.01排序后,也就是步长最一致的概率
5Top 1 差值数值13攻击者通常步长很小(如1)
6差值概率 Top N 和0.740.02步长分布的集中度
7小步长出现频率0.730.01比如步长<10的占比,反映遍历意图
8步长熵归一化值0.320.99核心指标:差值维度的混乱度

算法落地:为什么选择随机森林?

特征提取出来了,最后一步就是分类:是人,还是鬼?

考虑到为了保障用户体验,我们需要极高的准确率(Precision)以避免误杀,同时需要不错的召回率(Recall)。既然我们已经能够通过日志比较容易地标记出“黑样本”(有规律的)和“白样本”(正常的),这显然是一个典型的有监督学习场景。

我选择了 随机森林 (Random Forest) 算法,理由很简单:

  1. 抗过拟合:它由多棵决策树构成,泛化能力强。
  2. 解释性强:我们可以知道哪个特征(比如是步长熵还是前缀熵)起了决定性作用。
  3. 处理非线性:它能很好地处理这些复杂的概率特征。

Python 训练示例

使用 sklearn 库,几行代码即可完成模型的训练:

from sklearn.ensemble import RandomForestClassifier

# 初始化模型
# n_estimators=100: 构建100棵决策树
# max_depth=5: 限制树深,防止过拟合
rf = RandomForestClassifier(n_estimators=100, max_depth=5, random_state=42)

# 开始训练
# X_train: 我们提取的那8个维度的特征矩阵
# y_train: 0(正常) 或 1(爬虫) 的标签
rf.fit(X_train, y_train)

# 验证效果
print("Accuracy on training set: {:.3f}".format(rf.score(X_train, y_train)))

输出结果:

Accuracy on training set: 0.997

模型在训练集上达到了接近 99.7% 的准确率。这说明我们提取的“熵特征”非常强效,能够清晰地将爬虫流量与正常流量在多维空间中切割开来。

总结与思考

这次实战不仅成功遏制了恶意爬虫,更验证了一个观点:在应用安全领域,数学往往比复杂的规则更接近本质。

  1. 透过现象看本质:爬虫无论如何伪装 IP 和 UA,其“批量获取数据”的贪婪本性决定了其请求必然带有某种“低熵”的规律。
  2. 多维度打击:从“号码分布”深入到“差值分布”,再结合随机森林算法,我们构建了一个立体的防御模型,极大地提高了攻击者的伪造成本。

安全对抗是一场永无止境的博弈,希望今天的“信息熵”思路能为你提供一些新的灵感。如果你也有类似的反爬经验,欢迎在评论区交流!

文章目录

相关文章

暂无相关文章推荐