.jpg)
模型能力正在快速提升,但提升并不均衡。我们一直在与安全团队合作,帮助他们在自己的代码和开源软件中发现和修复漏洞,这些工作让我们对如何使用模型保护源代码有了更深入的理解。我们的核心发现:漏洞发现现在可以轻松并行化,瓶颈已经转移到验证、分类和补丁阶段。
为了说明这种差异,作为我们对开源软件扫描工作的一部分,截至 2026 年 5 月 22 日,我们已披露了 1,596 个漏洞。据我们所知,其中只有 97 个已被修补。
本指南将介绍如何使用 Claude Opus 构建威胁模型、发现代码库中的漏洞,然后进行验证、分类和修补。虽然我们没有所有答案,但我们将分享团队如何扩展发现能力以及后续阶段的有效方法。立即开始使用 配套仓库,其中包含交互式工作流的技能和自主扫描的演示工具;我们会在阅读过程中指出实现每个步骤的技能。
发现与修复循环
发现和修复最多漏洞的团队都采用了现有最佳实践的变体。我们将其提炼为六个步骤的流程:
- 威胁建模: 在开始扫描之前,先定义什么算作漏洞。
- 沙箱: 构建沙箱环境来隔离代理并验证漏洞利用。
- 发现: 让模型在源代码中查找漏洞。
- 验证: 独立确认哪些发现实际上是可利用的。
- 分类: 去重发现、分配严重程度,并确定修复优先级。
- 修补: 应用修复,确认漏洞已被消除,并搜索变体。

在威胁建模和沙箱构建上的一次性投入,为防御者的循环提供了动力——这是一个发现、验证、分类和修补的重复循环——其中的瓶颈不是发现漏洞,而是发现之后的所有工作。
前两个步骤——构建威胁模型和沙箱——是其余循环的准备工作。这些通常在每个代码库上做一次,当底层系统发生变化时再重新审视。接下来的四个步骤是你要对源代码运行的循环:发现、验证、分类和修补。
在代码库上的第一次运行通常会产生最多的发现。后续运行通常会发现更少——但往往更复杂——的漏洞,因为简单的漏洞已在之前的运行中被修补。但是,不要期望第 n 次运行会没有新发现。模型具有随机性,大型代码库可能有一条长长的漏洞尾迹,即使代码未更改也会持续出现。
在对代码库的第一次迭代中,你应该多次运行该循环,根据净新发现的数量和对该系统的风险承受能力来决定何时停止。第一次迭代之后,继续定期扫描,或者在代码发生重大变更时扫描。
接下来,我们将详细介绍每个步骤,解释其重要性、产出内容以及实施方法。
1. 威胁建模:定义什么算作漏洞
导致误报最常见的原因是模型对你的信任边界缺乏良好理解。模型可能将代码标记为易受攻击,因为它假设客户端可能发送损坏的值或攻击者可能控制配置,尽管这些输入在你的环境中是受信任的。反之,模型可能假设面向互联网的服务是仅限内部的,从而低估真实漏洞。在这两种情况下,模型错误的是威胁模型,而不是代码。
一个团队注意到他们发现中的一个模式:模型在拥有完善威胁模型文档、系统设计文档、需求和约束条件的系统上表现最佳。当威胁模型定义明确时,模型的发现"90% 的情况都是可利用的"。
你可以通过两个步骤与 Claude 一起构建威胁模型:
首先,从代码、文档和漏洞历史中进行引导。 向模型提供你会给新安全工程师第一天看的内容:架构文档、Wiki、入口点、Git 历史和过去的漏洞。这有助于克服仅从代码推断隐含知识、权衡和设计决策的挑战。然后,让模型创建一个包含系统上下文、资产、入口点和信任边界的威胁模型。最后,让模型对过去的漏洞进行聚类并列出相关的漏洞类别。确保威胁模型记录了你关心和不关心的漏洞以及原因。
一个团队审查了数百个过去的 CVE 和安全修复提交,将其提炼为"漏洞形状"提示,并让模型回答两个问题:修复是否完整?是否在所有其他地方都应用了?他们在一小时内发现了三个可利用的问题。正如他们所说:"'人们过去利用过什么'有时比'在这个代码库中找到漏洞'是一个更容易的捷径。"
其次,让模型面试了解系统的人。 考虑 Shostack 的四个问题:我们在构建什么?什么可能出问题?我们对此做了什么?我们做得好吗? 先运行引导步骤,这样被面试者不必从零开始。这样,他们可以基于草稿开始,而不是花数小时研究和构建威胁模型。虽然面试步骤是可选的,但它添加了模型无法从代码或文档中获取的上下文,从而改进威胁模型。
一些实践可以产生很大差异:
- 考虑你的依赖项的安全策略。 许多开源项目发布了安全策略。例如 vLLM 的 `security.md`、SQLite 的"Defense Against the Dark Arts"和 ImageMagick 的安全策略。你的威胁模型应该直接参考它们,而不是从头重建策略。
- 命名什么是受信任的。 如果你信任配置文件或经过身份验证的客户端,在威胁模型中记录它。这些假设有助于将不可利用的 Bug 与实际漏洞利用区分开。
- 在代码中包含 `THREAT_MODEL.md`。 将其放在仓库中并随代码变更更新。发现代理可以在搜索之前阅读它,跳过已知的非问题。
你将在两个地方使用威胁模型。在发现阶段,作为范围界定:划分代码、确定目标优先级并跳过范围之外的内容。这有助于处理无法完全扫描的大型代码库。在分类阶段,作为过滤器:在广泛扫描后,使用威胁模型更好地校准严重程度以适应你的系统和环境。
一个扫描大型项目的团队有 40% 的误报率,他们深入调查原因。发现是可重现的,PoC 证明了可利用性。但拥有该代码的开发团队将其视为误报,因为这些 Bug 不符合项目的威胁模型。另一个团队的 CISO 简洁地总结道:"[模型]对代码有很好的理解,但对我们没有很好的理解。"
试试 threat-model 技能。 它引导完成本节描述的两个步骤——`bootstrap` 从你的代码、CVE 和 Git 历史中推导草稿,interview 引导系统所有者完成 Shostack 的四个问题来完善它。输出是一个 `THREAT_MODEL.md` 文件,用于发现和分类步骤。
2. 沙箱:安全运行代理并验证可利用性
沙箱的一个目的是保护你的系统。 为了让模型安全自主地运行,你需要一个强大的隔离层。没有它,代理可能超出目标范围并做出意外行为。
一个团队告诉模型它没有网络访问权限——但实际上有——模型发现它可以从 GitHub 获取内容。另一个团队观察到代理在扫描过程中回答了一个 GitHub issue。这两个行为都不是恶意的,但都证明了需要通过代码和配置强制执行约束。
将隔离级别与你的威胁模型匹配。容器对于读取代码的发现代理来说是可以的,但在微虚拟机(如 Firecracker)或完整虚拟机中运行目标及其 PoC,锁定出站流量,使其无法到达你的生产系统。永远不要让凭据(`/.aws`、`/.ssh`、`.env`)对代理可用。
仅在设置期间给沙箱网络访问权限。拉取依赖项、构建、安装工具、部署目标并运行现有测试以确认一切正常。然后,快照环境并移除网络访问。在扫描期间,只允许到模型 API 的流量,通过本地代理路由。在每次运行开始时加载快照,以便每次扫描都从相同的干净状态开始。
沙箱的另一个目的是证明可利用性。 在静态扫描期间,模型读取代码并假设什么可能出错,但它无法测试路径是否可达或是否存在补偿控制。因此,模型可能标记你实际上不关心的不可利用的代码正确性 Bug。当团队构建了代理可以编译代码、运行测试和触发概念验证的沙箱时,不可利用的发现显著减少。
一个攻击性安全团队构建了一个工具,为代理提供测试平台,使用简单的验证规则:只有当代理能构建概念验证并在测试平台上运行时,才是真阳性。他们六周后的评估是"最大的效果杠杆是为模型提供测试平台、实时系统和运行 PoC。"
构建沙箱时,尽可能固定一切,使每次运行在相同环境中使用相同代码:镜像标签、提交 SHA、依赖项和构建命令。缓存本地副本,使构建无需网络,目标是使容器持久化,以便多个测试循环可以直接加载。
一个团队的扫描标记了一个漏洞,但结果发现是代理下载了旧版本的库而不是实际部署的版本的副产品。一位阅读记录并发现正在下载不同依赖项的工程师发现了这一点。他们现在构建的 Docker 容器将依赖项固定为与生产匹配,因此发现代理和验证代理在攻击者会使用的相同构件上操作。
重要的是构建足够忠实于生产的沙箱。排除依赖项(如队列或数据存储)可能导致少报生产中可能存在的 Bug。反之,忽略生产防御(如 WAF 或认证网关)会导致模型报告生产环境已缓解的不可利用发现。
尽管如此,如果由于云依赖、数据存储或其他现实世界的复杂性,构建代表性沙箱不切实际,请先从下面的发现步骤开始。你不一定需要在沙箱中运行 PoC。前沿模型擅长仅通过分析源代码发现漏洞。包括我们自己在内的几个团队发现这很有效。权衡在于验证阶段,没有运行目标,我们无法用 PoC 证明发现,因此在验证上预算更多时间。你可以在发现量证明合理后再投资沙箱。
参考工具 `README.md` 中的沙箱实现。 在这个实现中,代理和目标在 gVisor 隔离的容器中运行,出站流量锁定到模型 API。目标从固定到特定提交的 Dockerfile 构建,`setup_sandbox.sh` 处理设置阶段。
3. 发现:提供丰富上下文、简短提示和有用工具
给发现代理按需加载的上下文访问权限,如威胁模型、架构文档和过去扫描的结果。当代理理解你的信任边界和系统实际部署方式时,它可以更好地识别特定于你系统的漏洞。
我们发现前沿模型在发现阶段受益于越来越简单的提示。反直觉的是,更规定性的提示会使发现变差——长长的检查清单往往会减少模型的创造力并产生更少的新颖 Bug。以下是在发现阶段有帮助的提示技巧:
- 提供目标和上下文。 说明"为什么"和"什么"——为什么扫描,什么样的发现是重要的,正在扫描什么系统——把"如何扫描漏洞"留给模型。前沿模型在安全任务上越来越强,过度规定性会缩小它们的尝试范围。
- 尝试请求特定漏洞类别。 如果你想专注于由过去 CVE 或代码库语言引导的特定类型漏洞,请明确说明。描述漏洞类别、它的作用以及它倾向于出现的位置,以便模型在你的代码库中识别它。
- 定义输出。 要求具有预定义字段的结构化报告,并对它们排序使模型的推理基于每个字段。示例字段包括理由、发现、影响、严重程度等。包含逃生舱口以便模型可以提前退出弱发现。
给模型搜索和阅读代码库的工具,如 grep、glob 等。也允许模型使用你的团队可能使用的安全特定工具,如 SAST 扫描器或模糊测试器。询问模型特定任务需要什么工具并使其可用。最后,让模型根据需要构建工具:最近的前沿模型越来越擅长编写它们需要的工具。
除了源代码,一个渗透测试团队还给了发现代理发送请求、检查响应和查询流量日志的工具。因此,代理不需要猜测路径是否可达,可以针对运行中的应用程序测试每个候选项,将真阳性率提高到近 100%。
让模型对系统进行第一遍扫描以划分搜索空间,例如按攻击面、端点或组件。然后,将这些分区馈送给并行的发现代理,使它们不会收敛到相同的浅层 Bug。最后,运行系统级别的扫描,将分区级别的发现作为上下文来搜索漏洞。
尝试暴力发现的团队很快遇到了收益递减。来自一个团队:"我们最初尝试直接横向扩展并发送更多代理,但看到了限制性回报。"另一个增加了焦点领域和并行代理的数量并获得了"大量问题",但大多数是彼此的重复。
如果你有沙箱来运行目标,要求发现代理构建发现的 PoC,如脚本、崩溃输入或失败的测试。构建 PoC 帮助代理迭代并确定发现,该工件为验证代理提供了具体的评估证据。尽管如此,代理无法重现的发现仍然可以报告,标记为未经证实,以保持高召回率。
`vuln-scan` 技能 在此阶段很有帮助。它读取你的 `THREAT_MODEL.md`,将目标划分为焦点区域,并为每个区域分出并行审查代理。输出是下一步直接使用的结构化发现。
4. 验证:过滤不可利用的发现
发现优化召回率;验证优化精确率。换句话说,发现应该尽可能多地发现漏洞——即使是不太可能的——而验证应该排除实际上不可利用的发现。当代理试图在同一步骤中同时执行两者时,它可能会自我审查并排除单独验证步骤会确认的真阳性。我们通过艰难的方式学到了这一点,要求发现代理也验证发现导致它们过滤掉单独验证步骤会确认的真阳性。
验证代理应独立于发现代理。在没有共享文件系统或对话历史的新容器中运行验证器。如果验证器暴露于发现代理的推理,它可能只是同意而不是测试声明。因此,只给验证器(1)概念验证或书面发现和(2)代码库,以便它可以搜索发现者遗漏的缓解措施(例如,上游验证、认证门控、类型约束或不可达代码)。
如果单次验证仍然让太多不可利用的发现通过,尝试运行多个独立验证器。它们可以考虑不同角度或使用不同模型运行。然后,采用多数投票。也可以考虑让单独的评判者来决定发现和验证代理结果之间的裁决。
提示验证代理去反驳发现代理的发现。让验证器假设每个发现都是误报并搜索发现错误的原因。包含验证代理可用于确定发现是否为真阳性的明确标准。当发现代理的输出不包含 PoC 时,这最为重要。目标是排除尽可能多的不可利用的发现,以减少手动审查的工作量。
在我们合作过的团队中,添加对抗性验证器大致将发现阶段的不可利用发现率减半。要求验证器也构建确认漏洞的概念验证将误报率降至接近零。这两个步骤共同显著减少了下游分类和修补的工作量。
如果你能在沙箱中充分重现生产环境(参见第 2 步),提示验证代理构建并执行可重现的概念验证(PoC)。如果 PoC 有效,你可以得出发现是可利用的结论。注意反过来不成立——未能产生有效的 PoC 并不证明是误报。
一个扫描开源包的团队构建了一个帮助闭环的验证步骤:扫描包、生成概念验证,然后部署使用该包并触发 PoC 的模拟应用程序。他们的看法是:"验证是最大的瓶颈,而 PoC 就是验证。"
5. 分类:按根因去重,按前提条件和影响排名
虽然验证确认发现是可利用的,但分类评估修补优先级。以前,当发现需要更多精力时,发现 Bug 的工程师也对其进行分类。现在,模型能够在午餐前找到上百个候选项,分类已成为瓶颈。
正确的分类有助于防止告警疲劳。如果你提交了太多重复或严重程度被夸大的 Bug,产品工程师可能会停止阅读它们,甚至那些需要立即修补的也是如此。开源维护者特别容易被未分类的发现淹没,因为他们接收来自许多依赖其软件的不同用户的报告。
多个团队分享了相同的教训:如果我们发送一堆大多数不可利用的发现给产品工程师,他们会失去对报告的信任并放弃。他们还优先处理严重和高危发现,以避免压垮下游工程师。其他团队发现了一个胜利点:将模型指向他们现有的积压——之前扫描器、之前模型、Bug 赏金计划的未解决发现——并在几天内清除了数百个过期项目。
要对发现去重,考虑根因。扫描器经常在多个调用点标记同一个 Bug 或报告单个根因的多个症状。这是一个实用的方法:首先,使用廉价的确定性筛选:相同文件、相同类别、漏洞行号彼此在十行以内。然后,让模型对剩余部分应用定性规则:
- 视为重复:相同根因的不同表述;在多个调用点报告的相同漏洞;在每个端点报告的缺失全局保护(如认证检查);在同一路径中标记的原因及其后果。
- 视为独立:同一文件中不同的漏洞类别;不同变量到达不同的汇聚点;一个辅助函数中的两个独立 Bug;两个端点上相同的缺失检查,但每个都需要自己的修复。
如果你的工具为每个发现生成 PoC 和补丁,另一种去重发现的方法是检查一个发现的补丁是否也解除了其他发现的 PoC。
去重后,根据以下因素评估每个发现的严重程度:
- 可达性。 攻击者是否可以从真实入口点到达此代码,还是只能从内部代码和端点到达?
- 攻击者控制。 不受信任的输入是否完整到达汇聚点,还是上游有什么东西在清理或约束它?
- 前提条件。 触发 Bug 需要什么:非默认设置、特定功能标志、攻击者需要命中的狭窄时间窗口?
- 认证。 未经认证的攻击者可以触发它,还是需要登录用户或管理员?
- 读取与写入。 攻击者是只能读取数据,还是也能修改数据?
- 影响范围。 如果 PoC 触发,谁受影响?一个用户还是所有用户,一个租户还是整个平台,用户空间还是内核?
要将评估标准转换为分数,让模型在分配严重程度之前写出每个问题的答案。先梳理证据可以防止模型锚定在 Bug 类别上("SQL 注入,所以是严重的")然后夸大严重程度来匹配。作为起点:零前提条件加未经认证的远程访问是严重或高危。一到两个前提条件,或经过认证的路径,是中危。三个或更多,或仅限本地的,是低危。根据你的系统调整阈值。
模型可能会因为缺乏足够上下文而夸大严重程度。它们可能不知道攻击者实际控制哪些输入,或者看不到补偿控制。作为前者的例子,如果由未经认证的请求触发,SQL 注入是严重的,但如果由仅限管理员的配置文件触发则不是问题。对于后者,阻止漏洞利用的上游 WAF 或认证可能仅从源代码不可见。
解决方案是在分类期间提供威胁模型,告诉模型在你的系统中你关心和不关心的漏洞类型。例如,澄清"我们信任经过认证的客户端"可以简化或消除一整类严重发现。
一个团队发现模型通常过于自信,除非有东西可以验证或对威胁模型中的预期有更多上下文。他们的修复是给分类代理与发现代理相同的威胁模型。
试试 `triage` 技能。 它同时执行验证和分类:每个发现的多投票验证、跨运行去重以及按推导的可利用性重新排名。输出是一个简短的、排名的、有归属的列表,而不是原始转储。
6. 修补:闭环并改善下一个循环的上下文
修补是你闭环并修复漏洞的地方。它还有助于根据验证的发现改进威胁模型——更新信任边界或需要更多审查的组件——并将过去的发现馈送到下一次扫描的上下文中。每个循环都强化代码库并使下一次扫描更加有据可依。
修补之前,编写一个在现有代码上会失败的新测试。然后,实施修复并确认相同的测试现在通过而不破坏其他任何东西。(是的,这是测试驱动开发)。如果你不添加测试,修复可能会静默回归,并且很难追溯证明 Bug 是真实的。
一个渗透测试人员发现他们生成的补丁质量参差不齐——有些好,有些差——直到工具告诉模型通过针对修补后的代码重新运行概念验证来验证补丁。通过给模型反馈来迭代,补丁质量大幅提升,节省了人工审查时间。
模型可能会在特定调用点狭窄地处理发现而不是根因。简单地提示模型识别并修复根因可能是有效的。然后,让模型在两个级别查找变体:(1) 相同模式,在其他地方有相同错误代码的其他调用点或副本,以及(2) 相同类,有一个 SQL 注入漏洞的代码库倾向于有更多 SQL 注入漏洞。用验证的发现和补丁更新威胁模型以闭环。
发布补丁之前,运行对抗性检查。让新的发现代理作为攻击者探测补丁以确认补丁是全面的。然后,简化生成的补丁以处理过于侵入性的补丁。最小补丁更容易审查且不太可能引入新 Bug。提示进行修复根因的最小变更——不要重构、不要顺便清理、不要重新格式化。
一个团队最常见的补丁失败:"推荐的补丁倾向于尽可能严格,以至于会中断与其他服务的连接。它会解决问题,但会破坏允许服务正常工作的依赖关系。"
你可以根据检查阶梯验证每个补丁,从最便宜的开始:
- 构建。 补丁编译并且新测试通过。
- 尝试重现。 原始 PoC 应该停止工作。这捕获无效补丁。
- 检查回归。 原始测试套件仍然通过。这捕获有缺陷或过度限制的补丁。
- 重新攻击。 一个新的发现代理运行对抗性检查。这捕获不完整的补丁。
最后,虽然模型可以编写补丁,但人类仍需要拥有它。生成的补丁可能以可预测的方式失败——修复症状而不是根因、阻止合法输入或移除依赖服务的访问权限。目标是尽可能多地验证每个补丁,使人工审查需要的精力更少。目标是帮助开发团队专注于模型可能不知道的细微之处(例如,即将到来的变更、代码风格),以最少的审查和补丁更新。
试试 `patch` 技能。 它使用分类输出并为每个发现生成候选拉取请求,由独立的审查代理检查每个变更。
开始使用
尝试自己运行循环。克隆 `defending-code-reference-harness` 并在 Claude Code 中运行 `/quickstart`。它会引导你完成交互式工作流,从威胁建模到扫描到分类,在一个演示目标上。仓库还包含自主工具和 `/customize` 技能来更新工具以适应你的环境。
然后,在你自己的代码上运行。选择一个服务或包。从代码和文档引导威胁模型,并进行面试。投资构建你环境的沙箱。扫描。用独立代理验证发现。根据你的标准进行分类并审查所有高危及以上的发现。修补。然后定期重新扫描。
你的第一次扫描会发现比你预期更多的发现。大多数将需要验证和分类。在为更多扫描预算之前,先为扫描之后的流水线做预算。
一些可能有用的资源:
- Claude Security:Anthropic 用于代理式漏洞检测和修补的托管产品。
- `defending-code-reference-harness`:包含交互式工作流技能和自主运行演示工具的配套仓库。
- `claude-code-security-review action`:在每个 Pull Request 上以 Claude 作为安全审查者的 GitHub Action。
- 威胁情报增强代理:构建一个针对威胁情报 feed 丰富入侵指标代理的 Cookbook。
- 漏洞检测代理:构建一个构建威胁模型、扫描漏洞并将发现分类为结构化报告的代理的 Cookbook。
展望未来
我们相信模型越来越容易在代码中发现和利用漏洞。因此,我们作为防御者的工作是在攻击者利用之前找到并修复代码中的漏洞。一些团队甚至将他们的工具连接到事件,例如 Bug 赏金报告触发自动变体分析、安全审查触发扫描并附上候选发现,或验证的漏洞更新静态分析工具以防止将来再次发生。
这项工作至关重要且高风险。但如果做得正确,这是一个更大、更有希望的转变的开始,我们将能够在攻击者利用之前找到并修复漏洞。
如果你想继续关注我们在网络安全方面的工作,请在此处注册我们的邮件列表。
致谢
由 Eugene Yan 和 Henna Dattani 撰写,Michael Molash、Abel Ribbink、Justin Young、Ben Morris、David Dworken 和 Hasnain Lakhani 贡献。本工作基于我们在 Anthropic 使用模型进行安全的经验以及合作伙伴和客户分享的宝贵见解,对此我们深表感谢。
评论
还没有评论
欢迎留下第一条评论,帮助这篇内容更快形成讨论。