关键词:API 安全、资产分类、特征工程、自学习
一、背景(Why)
货拉拉现网支撑着海量域名与 API 接口的日常调用,涵盖公开、内部及第三方等多维场景。面对每日极为庞大的请求流量,我们的 API 体系规模仍在保持稳步扩张。API 作为承载应用程序的核心业务逻辑和敏感数据的角色,资产庞大形式多样,理论上每个 API 都可能是潜在的安全风险敞口,随着业务的发展网络安全团队面临的矛盾除了要解决“如何防护”,还需要更前置的理清楚要“防护什么”。
由此引出了本次分享的主要背景:API 安全主题下资产分类实践,如何给 API 贴上业务标签——注册、登录、订单交易等,有了资产分类标签,后续异常检测、基线告警工作才能“对症下药”。
- 🔐 注册/登录接口 → 防批量注册、暴力破解
- 📱 短信验证码接口 → 防短信轰炸、验证码爆破
- 📦 订单接口 → 防薅羊毛、恶意刷单
- 📁 文件传输接口 → 防敏感文件泄露
二、现状(What)
给定一条 API 的元信息( URL 、Method、请求头、请求体、响应体等),预测其所属的 业务 标签。
url : https://uapi.example.com/v1/user/login
method : POST
host : uapi.example.com
api : uapi.example.com/?_m=login
request_headers : {"Content-Type":"application/json",...}
request_body : {"username":"xxx","password":"xxx"}
response_body : {"code":0,"msg":"success","token":"..."}
response_headers : {"Set-Cookie":"...","Server":"nginx"}
这是一个典型的多分类任务,主流的资产分类方案:
| 方案类型 | 代表产品/思路 | 核心技术 | 优势 | 局限 |
|---|---|---|---|---|
| 规则引擎 | 传统 WAF 的 API 分类插件 | URL 正则 + 白名单 | 精确控制、无需训练 | 新 API 无法覆盖、运维负担重 |
| 流量 画像 聚类 | Salt Security 等流量分析平台 | 对 API 请求-响应对做无监督聚类 | 无需标注数据、可发现影子 API | 聚类结果需人工解读、难以对齐业务标签 |
| LLM 语义理解 | Noname Security(2024后引入LLM) | 大语言模型理解 API 语义 | 语义理解强、Zero-shot 能力 | 推理成本高、延迟大、私有数据安全隐患 |
| 端到端分类器 | 国内部分安全厂商 | ML 分类器 + 规则后处理 | 可离线批量标注、精度可控 | 标注数据依赖、缺乏持续进化机制 |
| 本方案 | 自学习 CatBoost Pipeline | CatBoost 文本特征 + 自学习标签回流 | 标注 效率高、持续自进化、精度+覆盖双优 | 依赖初始标注种子集 |
首要面临的是样本标签稀少、类别不平衡问题。标签从哪里来?我们在先前的专项治理中有过零散的涉及,依靠人工筛选打标,有时动辄还会拉上多个业务方协助,过程中积累了多个业务场景下的宝贵的基础核心标签数据。但是 API 数量庞大且伴随业务迭代更新,长期角度来看过度依靠人工标记不可持续。
为此,我们构建一套自动化、可自我迭代的分类系统机制,实践的内容包括特征工程迭代和自学习标签回流机制,最终实现了置信度 ≥ 95% 的 API 标签约 10,000 个、模型精度提升 5 个百分点的落地成果。
三、实践(How)
特征工程:从“数据说话”到“知识引导”
API 请求数据通常包含 URL、请求头(headers)和请求体(body),这些字段既包含业务语义,也包含大量噪声。
v1:暴力枚举 Key
基于不同的业务下的 API,请求和响应的 JSON key 不同且具有差异的假设,如下图,通过暴力提取左图头部和 body 数据中的 key (右图)合成 One-Hot 向量直接给分类模型,这样每个 API 都有一个高维稀疏的特征向量。
实际中,V1 版本特征工程反馈了重要的缺陷:高维稀疏的 0-1 特征向量维度数等于所有出现过 key 的数量,意味着上万个 key 中存在大量的噪声,比如 data、code 这类的常见的 key 在每个 API 下都有出现;由于把递归提取的唯一 key 作为特征向量,如果新的 API 中出现了新的 key,模型几乎没有泛化能力,而且复杂度特别高。
比如,训练集中所有注册接口恰好包含 invite_code 的 key,模型学到了 invite_code=1 -> 注册的信号,但线上遇到一个没有 invite_code 的 key 注册接口时,模型就会误分类。建模初期,在样本数量和类目不多的情况下,用这种方法可以快速验证,训练数据拟合效果很好,准确率 80%+,F1 值表现较差。
v2:手动构造 + 业务关键词列表
考虑到暴力枚举 key 存在的维度爆炸和泛化差的缺陷,在 v2 特征工程构建中参考了人工在手动给 API 打标签的经验,通常会观察 url 结构特征,比如长度、查询参数个数,又比如会考虑请求头、请求体中的参数,包含设备参数、用户参数更有可能是订单交易类业务,还有考虑到 url 的路径深度、参数数量、特殊字符 "-", "_", "?", "=", "&", "/", "." 分布特征。新增了布尔和计数类型特征。
# URL基础统计
url_length = len(url) # URL总长度
url_depth = len(path) # 路径深度
url_params_counts = len(query_params) # 查询参数个数
# URL特殊字符统计
special_chars = ["-", "_", "?", "=", "&", "/", "."]
special_chars_count = [url.count(char) for char in special_chars]
# URL关键词匹配(精确词边界匹配)
pattern = r'\b' + re.escape(keyword) + r'\b'
exists = 1 if re.search(pattern, url_lower) else 0
counts = len(re.findall(pattern, url_lower))
除此之外,v2 在 9 个业务类别下定义了关键词词典来捕捉最强的业务信号,并且在关键词匹配上做了跨源匹配。同一个关键词在不同字段出现代表了不同的信号,比如 login 出现在 url 中是非常强的信号,如果出现在响应体的某个字段中则比较弱。
business_keywords = {
"register": ["register", "join", "signup", "enroll", ...],
"login": ["login", "auth", "logout", "account"],
"sms_code": ["send", "sms", "verification", "phone"],
...
}
对比 v1,该版本的特征向量维度从上万维降低到几百维,在维度下降的情况下,利用从 url 字段提取的统计特征解决了泛化能力差的问题。通过引入业务词典、url 结构特征、请求头特征工程组合,我们将少数类 F1 从 0.25 左右提升到 0.80 以上,整体准确率达到 90%,泛化能力显著提升,但关键词词典需要持续维护。
v3:文本 + 统计特征混合
v2 版本的特征工程参考了手工打标,引入了人工维护的关键词词典,用一种精确匹配的方法理解 API 的请求数据而没有利用语义理解,比如订单交易类别的业务中,order_detail 和 orderDetail 代表了两种不同风格却属于同一个类别的 API,通过维护关键词词典捕捉这类的信号成本很高。
前面提到其实不断维护关键词特征成本会越来越高,如果可以将 API 请求参数数据序列化为文本,让模型自己去学习其中的语义信息,那么问题就可以迎刃而解。
CatBoost 原生支持 text_features 参数,能够自动从文本列中学习 BoW / TF-IDF / 嵌入特征,将 URL path、request body 的 key、response body 的 key 拼接成一个统一的文本字段。例如对于一个注册接口,生成的文本可能是:
v1 user register username password invite_code code msg data token
CatBoost 的文本处理器会自动对这段文本做分词、统计 TF-IDF、构建词袋特征,甚至可以学习词的组合模式(n-gram),构建的特征信息流如下:
由于请求头主要反映了调用方的身份而非反映 API 业务本身,在 v3 版本的特征工程中摒弃了 v2 构建的请求头特征如 is_brower 、has_auth 、header_field_exists。
自学习闭环:模型持续进化
整个自学习迭代机制由三个阶段组成:
Step 1:离线特征与初始训练
主要流程:
- 数据准备:模型“冷启动”的历史标注数据集和已经标注的 API 数据集
- 特征提取:序列化文本特征、关键词列表、结构化统计特征
- 模型训练:导出模型文件
- 离线任务部署:离线批处理任务
Step 2:置信度分层
部署离线任务之后,定期对新增的未打标的 API 进行批量预测,该步骤中为了让高置信度的标签回流至样本库,阈值太高回流标签少,太低则失去意义,测试了不同阈值下未标签的正确率与覆盖率,最终选择 95% 置信度作为回流阈值,为训练集提供了增量数据。
| 置信度阈值 | 伪标签正确率 | 可用样本比例 |
|---|---|---|
| ≥ 0.99 | ~99.5% | ~30% |
| ≥ 0.95 | ~97.8% | ~55% |
| ≥ 0.90 | ~94.2% | ~70% |
| ≥ 0.80 | ~88.5% | ~85% |
Step 3:高精度标签回流
模型预测之后,95% 的置信度是设定样本分流的阈值,不过置信度并不是唯一的标准,比如功能有相似的接口模型给出的置信度很高但是标签仍然是错误的,对于高置信度的样本抽取 5% 的样本人工验证后进入训练集才能防止这种偏差。
对于低置信度的样本,借助 SHAP 工具在复核中可以了解到每个部分特征的贡献度,辅助人工快速复核纠正标签回流到高精度标签集。
即便是人工手动对 API 打标也会存在判断模糊的情形,如果模型无法判断的标签进入到训练集之后会进一步强化它的偏差,形成恶性循环。所以在回流机制的运行中,要非常小心维护高精度标签集,可以适当牺牲更新速度换取精度。
四、总结
| 指标 | 结果 |
|---|---|
| 端到端 Pipeline | 从原始流量到模型权重的自动化构建 |
| 高置信 API 标签产出 | ~10,000 个(Conf ≥ 95%) |
| 模型精度提升 | +5 P.P. |
| 标注人力节省 | 相比纯人工标注 |
| 自学习回流 | 持续运转的数据飞轮已建立 |
经验教训
1. 伪标签陷阱
如果模型对某个类别存在系统性偏差(如把部分 config_setting 误判为 login),高置信伪标签会放大这个错误——错误标签加入训练集后进一步强化偏差,形成恶性循环。
解决:每轮重训后检查混淆矩阵变化,一旦某对类别互混率上升,立即暂停该类别的伪标签回流。
2. 固定阈值的幻觉
初期我们对所有类别使用统一的 0.95 阈值。后来发现,login 类别的 0.95 几乎 100% 准确(因为 URL 特征极强),而 config_setting 的 0.95 正确率只有约 90%(因为该类别 URL 风格多样)。
解决:引入"按类别动态阈值",每个类别单独评估最优阈值。
3. 难例太多反而有害
难例精标效果好,但有一个阶段过于追求难例数量,导致精标样本占训练集比例超过 20%。模型开始过拟合这些边界样本,在"简单"样本上精度反而下降。
解决:控制精标样本占总训练集 ≤ 10%。
收获
回顾以上的实践过程,总结下来主要的收获有两点:
1. 特征工程决定了下限,自学习机制决定上限
好的标签回流机制可以让模型不断修正边界,通过修正标签,让数据生产数据,精度提升精度。
2. 专家领域知识非常重要
模型选择并不一定需要非常复杂,关键点往往是工程师对于业务的理解,比如构建关键词列表时能够理解哪些关键词是强信号、请求体和请求头哪些字段更有区分度、哪些类别容易混淆,这些操作比不断调试参数更加有效。
展望
下一步我们计划引入自动化触发重训(取代当前的手动定期模式)、探索标签回流过程中使用 AI 工作流减少人工接入成本、基于公司现有的平台微调轻量级的 LLM。
相关链接: