作者:Batu Ozturkler & Shengyu Fu
排版:Alan Wang
GitHub 的上下文感知型密钥分类器提升了检测质量,使加权接受率提高了 11.5%,并将扫描执行时间的中位数缩短了 86%。

密钥扫描只有在开发者能够信任检测结果时,才能发挥最佳效果。检测范围过小,真正的凭证就可能残留在代码仓库中;告警过于频繁,开发者又可能把时间花在调查那些并非密钥的字符串上。
通用密钥尤其难以平衡这两方面。同一个字符串既可能是真实凭证,也可能是文档中的占位符,因此,检测时必须理解其周围的代码和文本。
为了解决这一难题,我们专门针对 GitHub 的产品需求训练了一个密钥分类器:在满足大规模扫描所需的速度和效率要求的同时,区分真实凭证与外观相似的字符串。
在配套文章 密钥保护必须跟上软件开发的步伐 中,我们解释了为什么密钥保护必须跟上 AI 加速的软件开发节奏。在本文中,我们将深入介绍为这一目标提供支持的上下文感知型分类器:我们如何设计、训练和评估它,以便在减少噪声的同时发现更多真实密钥。
我们实现了什么
GitHub 密钥扫描主要在两个关键时机发挥作用:
-
推送保护会在推送被接受之前扫描变更,帮助阻止密钥进入代码仓库,并让开发者有机会处理检测结果。
-
推送后扫描则在代码提交后运行,持续扫描仓库内容,并在检测到潜在密钥时创建告警,以便开发者和安全团队进行调查和修复。
我们开发了一款快速分类器,与此前基于 LLM 的处理流程相比,它将推送后扫描执行时间的中位数缩短了 86%。这种效率提升使我们能够将通用密钥检测扩展到推送保护中,让开发者在推送变更之前就能收到检测结果,而不必等到代码提交之后。
UI-1
密钥扫描在检测到文件第 5 行存在密钥后,通过推送保护阻止了此次提交。
为什么密钥难以检测
结构化凭证通常具有独特的前缀、长度、字符集或校验和。而通用密钥可能不具备这些特征。它们的语法可能与构建标识符、校验和、资源哈希、示例值以及其他非敏感字符串重叠。此时,上下文就成为决定性信号。
这些情况属于难负例:它们符合凭证的某些模式,却并不是可用的真实凭证。增加更多规则可以过滤其中一部分,但每增加一个例外,都有可能掩盖真正的密钥。相比之下,经过训练的分类器可以综合文件路径、密钥类型、变量名称以及附近的代码或文本等信号进行判断。
因此,我们推出了一款采用两阶段处理流程的新扫描器:
-
候选项生成: 使用正则表达式过滤器扫描内容,以较高的召回率识别潜在密钥。这一阶段经过优化,能够快速、确定性地捕获范围广泛的凭证类模式。
-
上下文感知分类: 使用经过微调的 ModernBERT 分类器评估每个候选项及其周围的上下文。该分类器经过训练,能够识别区分真实密钥与误报的信号,包括占位符、示例、文档片段、生成内容和非敏感标识符。由于该模型体积小、在我们的推理基础设施上运行效率高,因此其服务成本远低于基于 LLM 的处理流程,扩展起来也更加简单。
实际效果:
-
用户接受率提高 11.5%,误报减少。
-
推送后密钥检测的执行时间缩短 86%。
-
与基于 LLM 的处理流程相比,服务成本降低超过 98%。
我们的处理流程如何工作
扫描内容时,正则表达式会广泛筛查,标记所有看起来像凭证的字符串,避免在第一步就漏掉真实密钥。随后,每个候选项都会交由经过微调的 ModernBERT 分类器处理。分类器会结合候选值、文件路径、变量名称及周围的代码进行判断,并估算该候选项是真实密钥的可能性。置信度较高的检测结果会生成告警,而占位符、测试样例和校验和等外观相似的字符串,则会在触达开发者之前被过滤掉。

这里最重要的设计选择是:模型并没有取代确定性扫描,而是对其进行补充。正则表达式仍然是快速查找候选字符串的合适工具。分类器则用于发挥机器学习最大优势的环节:区分"看起来像密钥"和"很可能是真实密钥"。
list-view-blog
该分类器会尽可能区分"看起来像密钥"和"很可能是真实密钥"。
一个难负例问题:那些几乎看起来像真实密钥的字符串
大多数误报并不是随机产生的,而是来自难负例:这些字符串符合凭证模式,却并非可用的真实凭证。例如,文档中的占位符、测试中的简单示例、经过脱敏的值、非敏感 ID,以及自动生成的测试数据。这些内容很容易被正则表达式匹配,却很难仅通过规则系统进行过滤,因为过滤规则也可能意外漏掉真实密钥。
ModernBERT 的优势在于,它能够将候选项与附近的代码和文本一起读取,从而理解上下文。与 LLM 相比,它能够提供相近的上下文理解能力,同时避免生成自然语言输出所带来的延迟和计算开销。例如,如果候选字符串周围出现了"example"(示例)、"dummy"(虚拟值)、"fake"(伪造值)或"replace-me"(请替换此值)等词语,那么它的风险可能低于部署文件中被赋值给环境变量的字符串。分类器通过带标签的示例学习这些上下文差异,而不需要工程师为每一种例外情况都编写一条新规则。
我们选择 ModernBERT,是因为它非常适合这一工作负载:模型体积紧凑、运行效率高,而且特别适合对与代码相关的文本进行分类。对于密钥扫描而言,模型需要针对较短的候选文本窗口进行经过校准的判断,同时满足高吞吐量和低延迟的要求。
因此,在处理流程的这一阶段,经过微调的编码器分类器比通用 LLM 更适合用于实际服务。我们可以直接围绕自身的决策边界训练模型,并使用与密钥扫描质量密切相关的召回率和精确率指标进行评估。
我们如何训练模型
最大的挑战是找到具有代表性的数据,尤其是那些仅凭语法特征容易产生误判的案例。我们的训练和评估策略结合了三个数据来源:
-
从产品中获取的示例: 来源于 GitHub 代码仓库,结合密钥检测功能中的用户反馈以及额外的人工验证。
-
通过高召回率正则表达式获取的候选项: 来源于 GitHub 仓库数据,包括经过人工标注的示例,以及用于扩大覆盖范围的弱标签数据。
-
针对稀少且困难的案例生成的合成示例: 例如文档中的密钥、测试样例、公钥、校验和、具有结构相似性的字符串,以及仅有细微差异的近似样例对。
合成数据在纳入训练集之前,必须通过确定性验证和基于模型的质量审查。训练重点关注真实密钥、符合实际情况的误报,以及那些经常使纯正则表达式方案产生混淆的难负例。每个模型输入都包含候选值、文件路径、密钥类型,以及限定范围内的周边上下文。我们通过离线评估选择分类阈值,并在全面推广之前,使用接近生产环境的流量验证模型行为。
在划分数据集之前,我们会将共享同一代码仓库、候选值或示例的数据行归为一组,避免近似重复样本同时出现在训练集和评估集中。
训练工作的重点是那些最影响告警质量的案例:真实密钥、符合实际情况的误报,以及正则表达式经常匹配到的难负例。输入表示包含候选密钥字符串、文件路径、密钥类型以及限定范围内的周边上下文。输出则是一个分类分数,供扫描器判断是否应继续处理该候选项。
我们的优化目标是平衡产品需求:在保持或提升召回率的同时,减少误报和服务成本。具体来说,我们先通过离线评估选择分类器阈值,再使用接近生产环境的流量验证效果,然后才进行推广。
训练过程中最重要的原则,是让模型充分接触那些容易造成混淆的示例。我们纳入了在语法上与真实密钥相似、但在语义上存在差异的候选项,让模型不仅学会密钥的外观特征,也能理解误报在具体上下文中的表现。
训练完成后,模型会导出为 ONNX 格式,并采用 8 位量化,从而实现快速推理。
评估结果

我们分两个阶段评估新处理流程在生产环境中的影响:首先,在相同的公开流量上与生产基线进行对比;随后开展面向客户的 A/B 实验。
-
离线评估 : 在推送后扫描场景中,使用相同的公开流量进行评估时,与此前的 LLM 处理流程相比,新处理流程的精确率提高了 13%,召回率提高了 19%。
-
在线评估: 随后,我们通过与此前的 LLM 处理流程进行 A/B 实验,验证这些质量提升是否能够转化为实际的客户收益。**按代码仓库所有者加权的接受率提高了 11.5%。**我们以代码仓库所有者为单位对接受率进行加权,以降低那些一次性批量处理大量告警的客户所产生的影响,否则,这类行为可能会扭曲简单平均值。
总结
最好的安全工具,是开发者愿意信任并持续使用的工具。更高的精确率意味着更少的干扰性告警;更高的召回率意味着能够捕获更多真实密钥;更低的成本则意味着我们可以持续扩大检测覆盖范围,而不必让扫描成本变得难以承受。
对于开发者和安全团队而言,这意味着误报更少,并且能够在密钥最容易被引入的环节获得更快的反馈,包括代码提交、拉取请求以及日常开发工作流。
混合式系统具有很大的优势。正则表达式能够提供速度、透明度和广泛的候选项覆盖;ModernBERT 则提供基于学习的上下文理解能力,并减少误报。在这一产品约束下,两者结合的效果优于单独使用其中任何一种方法。
我们还认识到,模型规模并不等于模型的实用价值。当任务范围明确、决策边界可以衡量,而且服务效率至关重要时,更小的微调分类器可能才是正确的选择。
这是我们提升密钥扫描准确性、可扩展性和易用性的一步。展望未来,我们将重点关注两个方向:
-
建立持续改进的闭环。 我们将利用生产环境中的反馈循环识别新的漏报和误报,并将真实场景中的信号转化为更高质量的训练数据和评估数据。
-
探索新的模型架构。 我们正在研究能够在单个步骤中完成密钥检测和提取的方法,在保持准确性和可扩展性的同时,降低处理流程的复杂性。
致谢
衷心感谢 GitHub 和微软的工程、科学及产品团队。感谢大家共同构建这款新型密钥扫描器所需的训练数据、评估体系、服务基础设施和推广计划。