ClaudeClaude Code

用 Claude 给自己的代码找漏洞再修好:官方六步法和参考仓库的 skill 实测,再加三轮“缩小攻击面”检查

整理官方讲用 Claude 找漏洞、修漏洞的两篇文章,在一个埋了 6 处问题的小服务上实测:参考仓库的 skill 从威胁模型一路跑到补丁,再照研究文章做三轮缩小攻击面的检查。6 处都找到了,另有几个我们没埋的真问题;4 个补丁都过了测试,但有一条回归测试失败的原因不对。直连官方约 13 美元。

发布 · 约 27 分钟读完 · 经人工审核()· 本站的内容怎么来的

本站已实测,测试日期:2026-10-21。

本页目录(13 节)
  1. 一、官方的思路:找到不难,难的是后面三步
  2. 二、参考仓库里有什么
  3. 三、本站怎么测的
  4. 四、第一步:让它先写威胁模型
  5. 五、第二步:扫描
  6. 六、第三步:验证和分级
  7. 七、第四步:出补丁,再自己过四关
  8. 八、换个角度:不找漏洞,先让用不着的代码碰不到
  9. 九、花了多少,用了多久
  10. 十、几件要注意的事
  11. 十一、该用哪一个
  12. 这次没有实测的
  13. 参考

这篇教程解决一个问题:想用 Claude 把自己的代码从头查一遍,找出漏洞,再修好,而且修完能证明真修好了。

本站之前写过三样现成的工具,它们和这篇的关系是这样的:

什么时候 用什么 本站文章
Claude 写代码的当下 security-guidance 插件,写完就查、当场改 安全检查插件
想查的时候,开箱即用 Claude Security 插件,几个代理扫全库,评审投票,再出补丁 Claude Security 插件实测
想自己搭一条流程,按自己的威胁模型和标准来 官方的方法文和配套的参考仓库(这一篇) —

现成的插件把流程封装好了,好处是省事,坏处是每一步怎么判断你插不上手。这一篇讲的是官方在 2026 年 5 月和 10 月发的两篇文章:一篇讲和多家安全团队一起找漏洞、修漏洞总结出的六步法,配了一个开源的参考仓库,里面有每一步的 skill;另一篇是 Anthropic 前沿红队的研究,讲换个角度,不去一个个找漏洞,而是把用不着的代码挪到登录或开关后面,让还没被发现的漏洞也碰不到。

本站在服务器上把这两条路都跑了一遍,对象是一个专门写的小服务,埋了 6 处问题。

步骤 结果
威胁模型 14 条威胁;顺手点出了 6 处里的 5 处,另有 2 个我们没埋、核实属实的问题
扫描 报了 8 条:3 处经典漏洞和“上传先落盘”都中了,另外两处各说中一半;我们放的几处干扰没有误报
分级 确认 7 条、去掉 1 条。不给威胁模型时,3 个高危被降成中危;给了之后升回来
修补 前 4 条的补丁都能用,打上后 28 个测试全过;有一条回归测试失败的原因不对
缩小攻击面三轮 两处攻击面问题加门,测试逐条不变;那条配置组合的问题也找到了,另有几处是我们没埋的
花费 直连官方约 13.3 美元,交互式那一次走酷站AI 约 4.3 美元,都是 Claude Code 按官方价的估算

下面细说。

一、官方的思路:找到不难,难的是后面三步

Claude 官方 2026 年 5 月的文章《Using LLMs to secure source code》是 Anthropic 和多家安全团队合作找漏洞之后的总结。核心判断只有一句:让模型找漏洞,现在很容易并行铺开;瓶颈挪到了验证、分级和修补。 文章给的数字是,截至 5 月 22 日,他们在开源软件里报出了 1,596 个漏洞,据他们所知修好的是 97 个。

文章把做法排成六步,前两步每个代码库做一次,后四步反复循环:

步骤 做什么 为什么
1. 威胁模型 扫之前先写清楚:系统是什么、哪些输入可信、哪些是攻击面、哪类问题算漏洞 误报最常见的原因是模型搞错了信任边界,把可信的配置文件当成攻击者能改的,或者把公网服务当成内网的
2. 沙箱 搭一个隔离的环境,智能体在里面跑,必要时在里面验证 一是防止智能体越界,二是能编译、跑测试、复现,不能利用的发现会少很多
3. 发现 给足上下文,提示词反而要短;先把代码分成几块,再分头找 检查清单写得越细,模型越放不开,找到的新问题越少
4. 验证 另起一个不看发现过程的智能体,默认每条都是误报,去找它错的理由 发现追求不漏,验证追求不错。同一个智能体两件事一起做,会把真问题自己筛掉
5. 分级 按根因去重,按能否触达、需要什么前提、要不要登录、影响多大来定级 一堆重复、定级虚高的报告送到开发手里,他们会连真问题也不看了
6. 修补 先写一个现在会失败的测试,再修,再跑全部测试,最后让新的智能体换个角度攻一次 模型容易只修报出来的那一处,不修根因;也容易修得太死,把正常功能挡掉

几条值得记住的经验,都出自这篇文章里各团队的反馈:

  • 威胁模型写清楚的系统,模型的发现九成可以利用。 反过来,一个团队 40% 的“误报”其实都能复现,只是不符合那个项目的威胁模型,被开发团队驳回了。
  • 翻历史修复是捷径。 有团队把几百个过去的漏洞修复整理成“问题长什么样”的提示,问模型两个问题:当年修全了没有,别处有没有一样的写法。一小时找到三个可以利用的问题。
  • 加一个唱反调的验证者,不能利用的发现大约少一半;再要求验证者把问题复现出来,误报几乎没有了。 但复现不出来不等于是误报。
  • 补丁要过四关,从便宜的到贵的: 能构建、新测试通过;原来的复现失效;原有测试全部通过;新开一个智能体再攻一次。
  • 补丁最后要有人负责。 模型写的补丁常见的毛病是修症状不修根因、把正常输入也挡掉、切断了别的服务要用的接口。

第二步的沙箱,文章说得很直白:一个团队告诉模型“没有网络”,其实有,模型照样从 GitHub 取了东西;另一个团队的智能体在扫描途中回复了一个 GitHub issue。限制要靠代码和配置强制,不能靠提示词。搭不出像样的沙箱时,可以先只做静态的发现,代价是验证要多花人力。

二、参考仓库里有什么

文章配了一个开源仓库 anthropics/defending-code-reference-harness(Apache 2.0,README 写明不再维护、不接受贡献)。里面分两部分:

部分 是什么 会不会执行目标代码
Claude Code 的 skill /quickstart、/threat-model、/vuln-scan、/triage、/patch、/customize,另有检测和响应用的 /dnr-hunt、/dnr-respond 前五个只读写文件,不构建、不运行、不联网。README 的说法是,只要在交互式会话里逐个批准工具调用,不用沙箱
harness/ 自动流水线 侦察、发现、验证、去重、报告、补丁一条龙,默认针对 C/C++ 的内存问题,用 Docker 和 ASAN 会。必须在 gVisor 容器里跑,出网只放行模型接口,不在沙箱里时拒绝启动

本站只实测了前一部分,四个 skill 串起来就是六步里的 1、3、4、5、6。自动流水线要装 Docker 和 gVisor,本站的测试服务器上没有装,这部分按 README 写,没有实测。

几个用之前要知道的细节:

  • 它的 README 建议把子代理的模型固定下来,export CLAUDE_CODE_SUBAGENT_MODEL=<模型>,免得会话用 Opus、分出去的子代理用了别的模型。
  • 仓库的 .claude/settings.json 只设了一个环境变量 ANTHROPIC_CUSTOM_HEADERS,让每个请求带上 anthropic-cyber-runbook: skills 这个请求头。仓库的文档说这是声明用途的标记,只是结构信息,不含内容,可以去掉。
  • skill 是项目级的,放在仓库的 .claude/skills/ 下。官方的用法是在这个仓库里启动 Claude Code,再把要扫的目录作为参数传进去,比如 /vuln-scan ~/code/my-service。

三、本站怎么测的

被扫的是本站专门写的一个小服务 notes:团队记笔记用,注册、登录、写笔记、给笔记挂附件、上传头像、从旧版导出文件批量导入。Python 标准库实现,8 个 Python 文件约 530 行,另有 24 个测试。README 写了部署方式(公网 VPS,直接监听 8080,谁都能注册)和每个配置项。

我们在里面埋了 6 处问题,清单只放在本地,没有放到服务器上,免得被扫描读到:

编号 类别 问题
C1 经典漏洞 搜索把关键词直接拼进 SQL
C2 经典漏洞 下载附件时文件名没检查,能读到附件目录外面的文件。git 历史里修过头像的同一种问题,附件这里没修
C3 经典漏洞 读单篇笔记只查登录,不查是不是主人
A1 攻击面:默认配置 旧版导入默认关闭,但请求体先被解析,然后才查开关和登录
A2 攻击面:过早可达 上传附件先把请求体写进磁盘,再查登录
A3 攻击面:配置组合 拒绝名单写主机名、又没开反查时,规则永远匹配不上,也没有任何提示

另外放了几处看着可疑、其实没问题的写法(排序字段用了 f-string 但来自白名单,上传文件名取了 basename 等),看它会不会误报。

示例应用的 git 历史有四次提交:第一次是整个服务,第二次修了头像的路径问题,第三次删掉误提交的 macOS 元数据文件,第四次是下面第一步生成的威胁模型。

实测环境
日期 2026-10-21
版本 Claude Code 2.1.304;参考仓库固定在提交 d3bea6b(2026-08-06)
系统 Ubuntu 24.04,Python 3.12.3
模型 Opus 5.5,effort 用默认值(交互式界面上显示是 medium)。/vuln-scan 打分的子代理跑在了 Sonnet 5.5 上(见第五节),从 /triage 起加了 CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1,子代理都是 Opus 5.5
连接方式 直连官方接口,用 claude -p 跑;第六节交互式重跑分级的那一次走酷站AI 中转站
权限 --permission-mode acceptEdits,Bash 只放行每个 skill 自己声明的命令;每次加 --max-budget-usd 封顶

skill 是在参考仓库的目录里启动的,示例应用用 --add-dir 加进来:

cd defending-code-reference-harness
export CLAUDE_CODE_SUBAGENT_MODEL=claude-opus-5-5
claude -p "/vuln-scan ../notes-app" \
  --add-dir ../notes-app \
  --model claude-opus-5-5 \
  --permission-mode acceptEdits \
  --allowedTools "Bash(rg:*)" "Bash(grep:*)" "Bash(ls:*)" "Bash(wc:*)" "Bash(head:*)" "Bash(file:*)" \
  --max-budget-usd 4 \
  --output-format json

claude -p 里可以直接写 /skill 名字,Claude Code 会先展开再执行,这一点 headless 文档里写着。几个 skill 默认要和你对话(/triage 开头会问信任边界和打分标准),在 -p 里没法回答,所以 /threat-model 用 bootstrap,/triage 加 --auto。

四、第一步:让它先写威胁模型

cd defending-code-reference-harness
export CLAUDE_CODE_SUBAGENT_MODEL=claude-opus-5-5
claude
> /threat-model bootstrap ../notes-app

本站实际跑的是等价的 claude -p "/threat-model bootstrap ../notes-app",参数和上一节一样。

/threat-model 有三种模式。bootstrap 只看代码、文档和 git 历史,自己推一份;interview 用 Shostack 的四个问题(我们在做什么、会出什么错、打算怎么办、做得好不好)访谈系统的主人;bootstrap-then-interview 先推草稿,再拿草稿去访谈,有人能回答问题时官方推荐这个。本站用的是 bootstrap。

结果是示例应用目录里的一份 THREAT_MODEL.md,约 1.2 万字符,八节:系统背景、资产、入口和信任边界、14 条威胁(每条有攻击者、入口、影响、可能性、已有的防护、证据)、不打算管的、要问主人的问题、出处、修补建议。

几处做得好的:

  • 从 git 历史里学到了“同类问题”。 它读到了修头像路径的那次提交,把“通过文件接口的路径穿越”列为第一条威胁,状态写“部分防住”,理由是 safe_join() 只用在了头像上。
  • 把可信的东西排除掉。 README 说配置文件由管理员写、是可信的,它就把“远程攻击者篡改配置”放进了不打算管的一栏,理由照抄 README。官方文章说误报最常见的原因是搞错信任边界,这一步就是在防这个。
  • 列了它答不了的问题。 比如 8080 前面到底有没有 TLS、服务用哪个系统用户跑、legacy_import 现在开没开。这些问题留给 interview 模式去问主人。

最出乎意料的是它顺手给的“线索”。这个 skill 的本职是写威胁,不是找漏洞,可它在读代码时把我们埋的六处里的五处都点了出来(SQL 注入、附件路径穿越、读笔记不查主人、导入在登录前解析、上传先落盘再查登录),还多给了两条我们没埋的:

  • 导入格式里 body-lines 写成负数,解析器会死循环。 我们用一条带超时的命令核实了:三行内容,5 秒内没有返回。更要紧的是,导入默认是关着的,可 import_v1 先解析、再查开关、再查登录,所以默认配置下,没有账号的请求也会走进这段解析代码。这条后面第八节还会说到。
  • 删掉笔记后附件留在磁盘上,而 SQLite 会把最大的那个笔记编号再发出去,下一个人新建的笔记可能看到上一个人的附件。

它在结尾写明这些只是读代码时看到的,没有运行验证,下一步交给 /vuln-scan。

官方建议把 THREAT_MODEL.md 提交进仓库,代码改了就跟着改;扫描和分级两步都会读它。本站照做了。

五、第二步:扫描

/vuln-scan ../notes-app

/vuln-scan 读目标目录里的 THREAT_MODEL.md,按里面的入口划分重点,每个重点派一个子代理去读代码。源文件少于 15 个时不分头,一个代理从头读到尾,示例应用就是这样。读完后再给每条发现派一个子代理,单独重读那段代码、打一个 1 到 10 的置信分。这一步只排序,不丢掉任何一条,去掉误报是下一步的事。它不构建、不运行,Bash 只能用 rg、grep、ls 这几个只读命令。

跑下来 22 轮,约 3 分钟,1.05 美元,报了 8 条:

编号 扫描定的级别 置信 是什么 对应
F-001 高 1.0 读单篇笔记不查主人,登录的用户能读到别人的私人笔记 C3
F-002 高 0.9 搜索把关键词拼进 SQL,能读到别人的数据 C1
F-003 高 0.9 附件下载的文件名没检查,能读到附件目录以外的文件 C2
F-004 中 0.9 格式不对的导入内容让解析器死循环,而且解析发生在查登录和查开关之前 我们没埋的;和 A1 是同一处
F-005 中 0.7 删掉的笔记的附件被新笔记继承 我们没埋的
F-006 中 0.7 上传先落盘再查登录,没登录的请求也会在磁盘上留下文件 A2
F-007 低 0.7 开了反向 DNS 后,按 IP 写的拒绝规则不再生效 A3 的一半
F-008 低 0.6 登录的响应时间和注册的“名字已占用”能看出用户名存不存在 不算问题,见下一步

我们埋的 6 处,C1、C2、C3、A2 都中了,A3 只说中一半:它看到了“开反查后 IP 规则失效”,没看到另一半,也就是默认不开反查时,主机名规则拿 IP 去比,永远不生效。A1 没有作为“攻击面”单独报,但 F-004 把导入在登录和开关之前解析写进了理由。我们故意放的几处看着可疑的写法,一处也没有误报。要说明的是,扫描读到了上一步威胁模型里的那些线索,这些命中不全是这一步自己的功劳。

有个和模型有关的细节。我们按 README 设了 CLAUDE_CODE_SUBAGENT_MODEL=claude-opus-5-5,可是打分的 8 个子代理跑在 Sonnet 5.5 上:会话记录里,主会话每次派活都自己写了 model: sonnet。Claude Code 文档写着,这个变量只是默认值,派活时指定的模型优先;要所有子代理都用同一个模型,还得再设 CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1(2.1.257 起)。下一步我们加上了。

六、第三步:验证和分级

/triage ../notes-app/VULN-FINDINGS.json --auto --repo ../notes-app

/triage 是这套 skill 里最用力的一步:先去重,再给每条发现派 3 个互不相干的验证代理,各自从“这是误报”出发去找反证,最后投票、定级、找负责人。默认开头会问四个问题:系统是什么、最坏的攻击者是谁、用什么打分标准、验证者意见不一时偏向少报还是不漏。--auto 跳过这些,用偏向少报的默认值。

这一步是全程最贵的:60 轮,约 7.5 分钟,5.47 美元,24 个验证代理都用 Opus 5.5。结果:

  • 8 条进,7 条确认,1 条去掉,没有重复。 每条的 3 票都一致。
  • 去掉的是 F-008。理由是这个服务本来就公开用户名:谁都能注册,头像也是按用户名不登录就能取。登录不限速是另一回事,算加固上的缺口。
  • 定级重排了。 死循环从中升到高,理由是不用登录、导入关着也会碰到这段代码。SQL 注入、路径穿越、越权读笔记从高降到中,理由是要登录;新编号复用的那条从中降到低,因为要好几件事刚好凑在一起才会发生。

降级那三条,它在报告最前面专门写了一段提醒:降成中危只是因为打分表把“要登录”算作一个前提,可这里注册是公开的,这一步对攻击者没有成本,这三条影响的是所有用户的数据,应该和死循环一起先修。它还说,交互式地重跑、给出“用户之间的数据泄露”这样的威胁,这三条会升回高。

这正好对上官方文章的一句话:分级的代理要拿到和发现时同一份威胁模型。看了 skill 的说明才知道,--auto 时威胁模型是空的,不会去读目录里的 THREAT_MODEL.md。

它还在报告里写了自己哪里没按 skill 做:排序本该给每条再派一个代理,可 24 个验证者跑完只剩约 2.5 美元预算(我们设的上限是 7),就由主会话自己排了;查负责人要用 git log,那条命令要批准,没跑成。

把威胁模型交给它,再分一次级

为了看清威胁模型的作用,我们在交互式界面里重跑了一次 /triage,不加 --auto,每条只投 1 票省额度。交互式的 Claude Code 用不了赠送的 API 额度,这一次走的是酷站AI 中转站。开头四个问题这样答:

问题 我们的回答
系统是什么、不可信的输入从哪来 面向公网的 Web 服务,HTTP 请求都不可信
最坏的攻击者是什么样、什么绝不能发生 勾了“用户之间的数据泄露”,再写一句:参照 ../notes-app/THREAT_MODEL.md;绝不能让一个用户读到别人的笔记、附件、会话令牌,或者接管任何账号;注册是公开的,要登录不算门槛
用什么打分 默认的“按前提条件推出高、中、低”
验证者意见不一时 偏向少报

打分标准和上一次一样,只换了威胁模型。结果:

发现 --auto,每条 3 票 交互式,给了威胁模型,每条 1 票
读别人的笔记 中 高
搜索的 SQL 注入 中 高
附件的路径穿越 中 高
导入的死循环 高 高
编号复用继承附件 低 中
上传先落盘 中 去掉
开反查后 IP 规则失效 低 低
用户名能探出来 去掉 去掉

前三条正是我们说的“绝不能发生”的事,有了这句话就升回了高危,报告里每条都写明对上了哪一条威胁。

“上传先落盘”这次被唯一的那个验证者判成误报,理由是“只是靠请求量写满磁盘”,属于 skill 规定不报的那一类;上一次 3 个验证者都判它是真的,理由是写进去的文件在攻击停止后还留着。官方文章说单个验证者不够时就多投几票、取多数,这里看得很清楚:一票的结论会摇摆。

这一次 Claude Code 估算 4.25 美元,约 12 分钟。每条只投 1 票,并没有比 3 票那次便宜多少:--auto 那次因为预算不够,跳过了给每条派一个代理来定级的那一步,这次没跳。交互式界面里我们批准了 3 次命令:两次是 cd 进示例应用再跑 git log,一次是验证者用管道串起来的 ls、grep、head。前一种 Claude Code 会单独提醒,理由是先进目标目录再跑 git,可能用上目标仓库里不可信的 hooks 和配置。扫的是别人的代码时,这个提醒值得认真看。

七、第四步:出补丁,再自己过四关

/patch ./TRIAGE.json --repo ../notes-app --top 4

/patch 读分级的结果,按严重程度取前几条,每条派一个写补丁的代理,要求找根因、顺手找同类、改动最小、补丁里带一个回归测试;再派一个评审代理,它只看得到文件、行号、类别和 diff 本身,看不到扫描时写的那些描述。补丁只写成 PATCHES/bug_NN/patch.diff,不会打到你的仓库上。README 说这是故意的:没有 --apply 这个参数,所以也没法被提示注入骗着去用。

为了省额度,我们只修前 4 条。32 轮,约 4 分钟,2.04 美元,4 个补丁都过了评审:

补丁 修什么 怎么修 代码改动(不算测试)
bug_00 导入的死循环 body-lines 是负数时报格式错误 加 2 行
bug_01 搜索的 SQL 注入 关键词改成查询参数 加 3 行,删 2 行
bug_02 附件的路径穿越 用仓库里现成的 safe_join,限定在这篇笔记自己的目录;越界返回 400 加 5 行,删 2 行
bug_03 读别人的笔记 改用现成的所有权检查,别人的笔记返回 404 加 3 行,删 5 行

都是在根因上改,而且都用了仓库里已有的函数,没有另起一套。它自己交代了两件事:bug_03 的 diff 头里行数是它手改的,应用前要检查;bug_00 的测试在修之前会卡死而不是失败,要加超时跑。它还提醒,导入仍然先解析、再查开关和登录,挪到前面是值得做的加固,但不在这个补丁的范围里。

官方文章说补丁要过四关。前三关我们在服务器上用脚本跑,不花额度:

关 做法 结果
能应用、能构建 四个 diff 单独、依次 git apply;py_compile 都能干净地应用,包括手改过行数的 bug_03;编译通过
原来的问题复现不了 把带新测试的测试文件放到没修过的代码上,逐条跑,每条限时 10 秒 4 条新测试都失败;死循环那条是超时退出
没有改坏别的 打上补丁后跑全部测试 28 个(原来 24 个,加 4 个新的)全部通过
换个人再攻一次 新开一个 claude -p,对补丁后的代码重新 /vuln-scan 见下

第二关有一个要留心的地方。路径穿越那条新测试,在没修过的代码上确实失败了,但失败的原因不对:它返回的是 404,而不是把附件目录外的文件交出来。那篇笔记还没有附件,它自己的附件目录不存在,带上级目录的路径在这一步就解析不下去。我们另跑了一个对照:先给笔记传一个附件,再发同样的请求,没修过的代码就把目录外的文件返回了,打上补丁后返回 400。补丁没问题,是它写的测试只覆盖了“没有附件”这一种情况,修之前那次失败是碰巧。拿到模型写的回归测试,要看它在修之前是不是因为对的原因失败。 这条测试应该改成先上传附件。

第四关我们用 --single --no-score 省掉了分头读和打分:15 轮,约 2 分钟,0.56 美元。结论是没有再找到 SQL 注入、越权和路径穿越,“每条查询都用参数,每个笔记接口都查所有权,读文件都走 safe_join”。它报的 7 条里,4 条是我们没修的那几条(编号复用、开反查后 IP 规则失效、上传先落盘、导入先解析),其余是之前没报过的低危,比如上传时文件名写 .. 会让请求抛异常。这条我们核实了,没修过的代码也一样,不是补丁带进来的。

最后一句官方说得很重:补丁要有人负责。 四个补丁我们逐行看过,都很小,可以直接合;要是补丁大到看不过来,就先让它缩到只改根因,不顺手重构、不改格式。

八、换个角度:不找漏洞,先让用不着的代码碰不到

Anthropic 前沿红队 2026 年 10 月 20 日的研究文章《Using Claude to reduce the attack surface of code》出发点是:一个个找漏洞、修漏洞扩展不了,修好一个严重漏洞,也说明不了剩下的还有多少。换一个做法,把某个部署根本用不着、却暴露在外的代码挪到合适的关口后面:从登录前挪到登录后,从默认打开改成明确打开,从协议的第一条消息挪到会话更深的状态。代码里没被发现的漏洞还在,但更难碰到,严重程度就降了,而且一次降低的是所有未知漏洞的风险。

他们用了三种方法:

方法 找什么
默认配置 出厂默认配置下就碰得到、但默认功能根本用不着的代码
配置组合 两个选项单看都合理,放在一起会悄悄关掉一个安全控制,或者让行为没有定义而代码仍然可达
过早可达 和安全有关、又比较复杂的代码,在登录这类关口之前、在用户没有任何操作时、或者在协议状态切换之前就会运行

他们用 Claude 查了 25 个开源项目,五百多万行代码,C、C++、Java、PHP、Python 都有,找出 493 处。几个结论:

  • 11 个需要登录才能用的项目,每一个都有本不该在登录前碰得到的代码。
  • 收获最大的是配置组合:162 种和安全有关的组合,程序照单全收,不报错、不警告,文档也没写会怎样;其中 94 种悄悄关掉了一个安全控制。有一种写法在 4 个互不相关的项目里各自出现:管理员用主机名写拒绝规则,而服务器默认不做反向 DNS 查询,拿来比对的只有 IP,主机名规则永远匹配不上,拒绝名单形同虚设。还有一对选项的冲突在一个软件里存在了 21 年。
  • 按占代码量的比例算,缩得最多的反而是 OpenSSH。10.4 版的 sshd 里有约 4,500 行新加的、实验性的 ML-DSA-44(一种后量子签名算法)代码,默认没有启用,管理员明确排除了也一样:sshd 先解析客户端发来的密钥,再检查这种密钥类型允不允许。这 4,500 行约占默认 sshd 登录前代码的 16%。他们把问题和补丁报给了 OpenSSH,10.5 版把检查挪到了解析之前;Claude 又指出 10.5 的检查有两处缺口,和维护者几轮来回之后,检查挪进了解析器内部,10.6 版发布。这次修改让未登录的客户端能碰到的代码少了约五分之一。

官方把三种方法做成了一个 skill,正在并进他们的开源扫描服务,还没有公开。文章介绍了它的做法:分三轮,每轮从空的上下文开始,只带上一轮留下的一小段;每一轮都先列入口清单,每条发现都用一次实际运行的跟踪来确认;默认配置的发现要设计一道门,并用项目自己的测试证明默认行为不变,会改变非默认配置的门留给人决定;另外两轮只提修改建议。文章也说,不用这个 skill,照这个方法做也多半有效。

本站就照这个方法写了三段提示词,三轮各开一个新的 claude -p 会话,只把第一轮写的入口清单交给后两轮。在示例应用的一份副本上跑,这份副本退回到了威胁模型提交之前,免得前面的结论影响判断。

第一轮:默认配置

提示词的要点(全文约 570 字):

1. 先列入口清单:网络客户端能发的每一种请求,从哪里进来,默认配置下会依次跑到哪些函数。
   写进 ASR/entry-points.md,不超过 40 行,下一轮会把它交给另一个会话。
2. 找出默认配置下碰得到、但默认开着的功能根本用不着的代码。每一条都要实际跑一次来确认:
   写个小脚本,通过 notes.app.App.handle 发请求,记录那段代码确实被执行了,不要只凭读代码下结论。
3. 对确认的每一条设计一道门,要求默认配置下程序的行为不变:改之前和改之后各跑一次测试,
   结果必须一样。会改变非默认配置下行为的门不要动手,列出来留给人决定。

跑下来 27 轮,不到 3 分钟,0.56 美元。它写了一个探测脚本,用 sys.settrace 记下每个请求实际执行到 notes/ 里的哪几行,再看磁盘上多没多文件、有没有发反向 DNS 查询。确认了两条,都加了门:

入口 改前 改后
POST /import,未登录,默认配置 解析器 37 行里执行了 29 行(合法内容)或 21 行(格式错误) 执行 0 行
POST /notes/<id>/attachments,未登录或不是主人 每次在 incoming/ 留下一个文件,最大 10 MB,永不清理 不落盘,响应码不变

两处的改法都是把检查挪到前面,git diff 一共 6 行。改前改后 24 个测试逐条一致。

它还主动报了一处“和要求不完全一致”的地方:默认配置下,格式错误的导入请求原来返回 400 和解析器的报错,现在返回 404。不跑解析器就没法给出 400,所以这个变化躲不开;它说明了 404 和 README、和现有测试的意图一致,还顺带指出原来的 400 本身就暴露了这个端点存在,然后把要不要回退留给人。这正是研究文章说的那条规矩:门不该改变默认行为,改变了的交给人决定。

三件会改变非默认配置的,它列出来没有动:打开导入后解析仍在登录之前;打开反向 DNS 后,每个请求都要先查一次,按 IP 写的规则也会受影响;server.py 读请求体没有上限。

第二轮和第三轮

后两轮照同样的办法,各开一个新会话,只带上第一轮写的入口清单:第二轮看“过早可达”,第三轮看“配置组合”。提示词要求这两轮只写建议和补丁、不改代码,确认时不联网,用到反向 DNS 的地方一律打桩。两轮分别是 1.28 美元和 1.16 美元。

我们埋的那条配置组合(A3)在第三轮里确认了,而且它注意到仓库自带的示例配置正好就是这个组合。两轮给出的补丁我们没有应用,也没有像第七节那样逐个过四关,所以这里不展开。

三轮合计约 3 美元,比前面扫描加分级的 6.5 美元便宜,看问题的角度也不一样:前面那条路一条条找漏洞,配置组合那一处只说中一半;这三轮不找漏洞,只看哪些代码在不该碰到的时候碰得到,A1、A2、A3 都确认了,另有几处是我们没埋的。反过来,三轮读代码时也看到了 SQL 注入和越权,只是不在它的范围里,列出来没有验证。两条路互相补,不是谁替代谁。

九、花了多少,用了多久

步骤 连接方式 轮数 用时 花费(美元)
/threat-model bootstrap 直连官方 36 4 分 20 秒 1.16
/vuln-scan 直连官方 22 2 分 51 秒 1.05
/triage --auto,每条 3 票 直连官方 60 7 分 32 秒 5.47
/patch --top 4 直连官方 32 3 分 57 秒 2.04
再扫一遍,--single --no-score 直连官方 15 2 分 13 秒 0.56
缩小攻击面,第一轮 直连官方 27 2 分 49 秒 0.56
缩小攻击面,第二轮 直连官方 46 6 分 13 秒 1.28
缩小攻击面,第三轮 直连官方 35 5 分 22 秒 1.16
交互式 /triage,给了威胁模型,每条 1 票 酷站AI — 约 12 分钟 4.25

花费都是 Claude Code 按官方价算的估计,claude -p 取自输出里的 total_cost_usd,交互式那次取自 /cost,和实际账单可能有出入。直连官方的 8 次合计约 13.3 美元。最贵的是分级:它要给每条发现派好几个验证代理,花费跟着发现的条数乘上票数涨。官方文章的提醒在这里很实在:第一次扫描找出的东西会比你预想的多,先给扫描之后的验证、分级、修补留够预算,再考虑多扫几遍。

十、几件要注意的事

  • 子代理的模型要用两个变量才固定得住。 只设 CLAUDE_CODE_SUBAGENT_MODEL 时,主会话派活时指定的模型优先,见第五节。要统一,再加 CLAUDE_CODE_SUBAGENT_MODEL_FORCE=1。
  • /triage --auto 不读威胁模型。 想让分级按你的威胁模型来,就在交互式里回答开头的问题,见第六节。
  • 一票会摇摆。 同一条发现,3 票全票确认,1 票时被去掉。要省钱可以先 1 票过一遍,被去掉的那几条再用 3 票复核。
  • 回归测试要看失败的原因。 见第七节。
  • 限制要靠环境,不能只靠提示词。 这四个 skill 只读写文件,官方说在交互式里逐个批准工具调用就不用沙箱;缩小攻击面那几轮要运行代码,我们是在服务器上一个专用的普通用户下跑的,事后又翻了会话记录,确认它们没有监听端口、没有真的发 DNS 查询。即便这样,第三轮还是自己报告说有一次测试把临时目录建到了工作目录外面(测试结束自动删了),提示词里明明写了不要。这正是官方文章那句话:限制要用代码和配置来强制。隔离的几种做法见《安全机制一览》和《沙箱》。
  • 被扫的代码是不可信的输入。 代码、注释、提交说明里都可能写着给模型看的指令。参考仓库的补丁代理会把来自目标的文字包起来、提醒模型只当数据看,但它的文档也说这只是缓解,不是保证。补丁一定要人看过再合。Claude Code 对“先 cd 进目标目录再跑 git”单独提醒,也是这个道理。
  • 会不会被拦。 本站这 9 次运行都正常结束,没有遇到拒绝。Claude 帮助中心的说法是,普通模型可以用来审代码、做威胁模型、修已知问题、在自己的代码里找漏洞;别的一些网络安全工作可能被安全分类器打断,做这类工作的安全从业者可以申请官方的网络安全验证计划。
  • 走中转站。 交互式的 /triage 走的是酷站AI,问答、后台子代理、检查点都正常,跑完没有报错。参考仓库的 .claude/settings.json 会给每个请求加一个自定义请求头,经中转站时它有没有被转发,本站没有核对。

十一、该用哪一个

  • 平时写代码:装上安全检查插件,Claude 写完就查。
  • 想要开箱即用的深度扫描:Claude Security 插件,流程都封装好了。
  • 想按自己的威胁模型、自己的标准,每一步都看得见、改得了:用参考仓库的 skill,按威胁模型、扫描、分级、修补的顺序走;分级在交互式里做,把威胁模型交给它。
  • 功能开关多、配置项多、有很多“默认关着”的老功能:再照研究文章的三种方法查一遍攻击面。这一步不需要先找到漏洞,挪走的是所有还没被发现的漏洞的入口。
  • 修完:补丁过四关,回归测试看失败的原因,最后由人负责合并。之后代码有大改动就再扫一遍,并把新确认的问题写回威胁模型。

这次没有实测的

  • 参考仓库的自动流水线(Docker、gVisor、ASAN,默认针对 C/C++ 的内存问题),以及 /customize、/quickstart、/dnr-hunt、/dnr-respond,按 README 写。
  • /threat-model 的 interview 模式。我们只在 /triage 里回答了它开头的问题。
  • 研究文章里官方自己的缩小攻击面 skill 还没有公开,本站用的是照文章的方法自己写的提示词。
  • 被扫的是本站写的一个 530 行的小服务。少于 15 个源文件时 /vuln-scan 不分头扫,大项目上分头扫描的效果没有测到。
  • 每一步只跑了一次。模型的结果有随机性,官方文章也说大代码库会一直零零星星冒出新的发现。
  • 只修了分级排在前面的 4 条;缩小攻击面第二、三轮的补丁没有应用和验证。

参考

来源

通过 API 中转站使用这些模型?到 Veridrop 免费检测中转站给你的是不是它声称的模型,或者看看中转站排行榜前 100 名。

相关文章