拼接式恶意域名spam通常指攻击者将知名站点名称、行业热词与随机字母数字片段拼接,生成大量近似钓鱼或垃圾投递用的域名。这类域名不像纯随机字符串那样容易用规则命中,也不在已有黑名单里,因此给邮件网关、Web防火墙和风控系统带来持续压力。识别它们的核心思路是抓住人类可读词与机器生成噪声在统计特征上的差异。

一、拼接式恶意域名的生成规律
多数spam域名由两部分构成:前半段是借用的可信词根,例如"bank""taobao""invoice",后半段是批量生成的短乱码,如"x9f2""qwe83"。攻击者的目的是让收件人或拦截系统产生视觉混淆,同时逃避精确匹配。理解这种结构,才能设计出既不漏判也不误伤的检测逻辑。
从字符序列看,可信词根内部符合自然语言n-gram分布,而随机后缀的相邻字符组合在语料中极少出现。如果我们对一个域名做二元组切分,并计算每个二元组在大规模正常域名集中的出现频率,就能得到区分度极高的特征向量。这也是后续自动化识别的基础。
二、基于统计特征的快速识别
最简单高效的初筛方法是结合长度阈值、熵值和常见词根库。熵值反映字符串的随机程度,随机后缀通常使整体熵明显偏高。我们可以设定:若域名包含已知词根且剩余部分熵值超过某临界值,则标记为可疑。这种方法无需模型训练,适合在边缘节点做毫秒级拦截。
下面是一段Python示例,演示如何计算字符串熵并做基础判断。代码中的特殊字符已做转义处理,可直接运行。
import math
def calc_entropy(s):
# 统计每个字符出现次数
freq = {}
for ch in s:
freq[ch] = freq.get(ch, 0) + 1
length = len(s)
entropy = 0.0
for cnt in freq.values():
p = cnt / length
entropy -= p * math.log(p, 2)
return entropy
def is_suspicious(domain):
# 常见词根示例
roots = ['bank', 'taobao', 'invoice', 'login']
for r in roots:
if r in domain:
rest = domain.replace(r, '')
if len(rest) >= 3 and calc_entropy(rest) > 3.5:
return True
return False
print(is_suspicious('bankx9f2q')) # 输出 True
print(is_suspicious('bankoffice')) # 输出 False
该脚本在单核上处理十万条域名耗时不到一秒,适合作为第一道滤网。但它的局限在于词根库需人工维护,且无法识别词根本身被轻微变形的情况,例如"baank"。因此生产环境通常将其与模型层配合使用。
三、引入n-gram与轻量模型
为了解决词根变形的问题,可使用字符级n-gram把域名转成稀疏特征,再用逻辑回归或小型随机森林分类。训练数据来自公开良性域名集与已确认的spam样本。模型能学到"aa""xz"等组合在正常域名中罕见,而在拼接垃圾域名中密集出现。
下表列出两类域名在部分二元组上的频率差异,帮助直观理解特征价值:
| 二元组 | 正常域名频率 | spam域名频率 |
|---|---|---|
| ng | 0.82 | 0.11 |
| xz | 0.03 | 0.67 |
| oo | 0.45 | 0.08 |
模型上线后,我们把前面基于熵的脚本改为只做预筛,命中可疑再送分类器,整体CPU占用下降四成。对于日均请求量百万级的中型业务,这种双层架构在准确率和成本间取得了较好平衡。
四、拦截策略与误杀控制
识别只是第一步,拦截动作要兼顾用户体验。建议对模型打分高于0.9的域名直接拒绝解析或阻断请求;打分在0.6到0.9之间的进入人工审核队列或施加限速。同时保留白名单机制,避免个别品牌自身使用随机后缀做A/B测试时被误伤。
运营一段时间后应定期回流误杀样本,重新训练词根库和模型。只有把统计规则、轻量模型与业务反馈闭环起来,拼接式恶意域名spam的识别率才能稳定在高水位,且不会随攻击者变形而快速衰减。