site logo

Marico's space

LLM 生成安全报告:我用自己的代码跑了一遍,发现漏报比误报更可怕

AI技术与应用 2026-04-24 11:29:26 5

最近安全圈有个消息挺有意思:Linux 内核维护者开始收到 LLM 生成的安全报告,115 票热度,评论区吵成一锅粥。大多数人在争论这些报告是不是噪音,但我更在意一个没人提的问题——漏报

抱着这个疑问,我决定在自己代码上跑一遍同样的流程,结果比我想的还刺激。

实验怎么做的

找了三段生产代码:Next.js 的 webhook 处理器、2021 年写的 auth 模块(那时候刚转开发,代码质量你懂的)、还有一个 Railway 环境变量包装器。用的提示词极其简单,就是让 LLM 扮演安全工程师,只列漏洞,不解释概念,不废话。

丢给 Claude Opus 4 和 GPT-4o 跑了一遍,结果很有意思——两个模型找到了相同的东西,但也都漏掉了同一件事。

它们找到了什么(真的)

Claude 在我 webhook 处理器里标出三个问题:

  • 入站 payload 缺少签名验证——真的,我知道但一直拖着没改,"以后"已经拖了两个月了。
  • console.log(req.body) 可能在某些请求里打敏感数据——真的,我没注意到。
  • 内存里的限流器(没上 Redis),容器重启就失效——真的。

GPT-4o 找到同样的三个,外加一个误报:说我用 Math.random() 生成会话 ID,其实那玩意是用于内部日志关联 ID 的,跟会话半毛钱关系没有。

三个真实发现,一个误报,看起来挺靠谱对吧?

它们漏掉了什么(这才是问题)

那个 2021 年写的 auth 模块里,有一个容易受到时序攻击(timing attack)的令牌比较逻辑:

// 这行代码看起来完全正常——但其实有安全漏洞
function validateToken(receivedToken: string, expectedToken: string): boolean {
  // ❌ 易受攻击:直接字符串比较
  return receivedToken === expectedToken;

  // ✅ 正确做法:常量时间比较
  // return crypto.timingSafeEqual(
  //   Buffer.from(receivedToken),
  //   Buffer.from(expectedToken)
  // );
}

两个模型都没标记它。一个都没有。

为什么?因为代码"看起来对"。函数返回布尔,比较两个字符串,命名清晰——快速扫代码的人和快速扫代码的 LLM 一样,直接就过去了。

真正的问题:假阴性给你一个心理安慰

当 LLM 给你三个真实漏洞,你的第一反应是"好,现在我清楚我的问题了"。但时序攻击漏洞还在那儿,隐形的,带着"AI 已审查"的章。

这就是我觉得最要命的地方:LLM 安全报告的危险不在于产生噪音,而在于产生信心。

假阳性你还能扔掉,顶多浪费点时间。假阴性——LLM 没看到的漏洞——给你一个更可怕的东西:一种"已经审过了"的错觉。你带着这个错觉就部署了。

LLM 看得好 vs 看得差的漏洞

测多了之后规律就出来了:

看得好的:

  • 代码里的硬编码密钥(API keys、明文密码)
  • 明显输入缺少校验
  • 字符串拼接 SQL 查询
  • 训练数据里有的 CVE 依赖
  • 日志暴露敏感数据

看得差的:

  • 依赖执行上下文的漏洞(竞争条件、时序攻击)
  • 需要理解业务模型的授权问题
  • 隐式访问控制逻辑(代码没做的,而不是它做的)
  • 两个模块之间的交互漏洞,LLM 看不到整体

最后一类我觉得最危险。安全审计也好、代码审查也好,LLM 很擅长评估它面前的东西,不擅长推理缺失的部分和系统涌现行为。

用 LLM 做安全审查的几个常见错误

1. 只给文件,不给系统

时序攻击是在单个文件的上下文里漏的。如果我把完整调用链——从端点到校验——一起给它,也许能发现。也许。

2. 把沉默当批准

"没发现什么" ≠ "什么都没有",它只是"在它处理的内容里没发现什么"。这个区别很重要。

3. 不指定威胁模型

没有上下文时,LLM 假设一个通用威胁模型。不知道对手是脚本小子还是有资源有时间支撑的团队。提示词故意保持中立——这也是我的失误之一。

4. 只信一个模型

GPT-4o 和 Claude 发现了不同的东西,这本身就说明两者都没有完整覆盖。拿它们当独立查询跑,再对比输出,比只信一个靠谱得多。

5. 不按代码类型迭代提示词

webhook 处理器和 auth 模块需要不同的提示词,本地上下文变了,相关漏洞也变了。

FAQ

LLM 能替代真正的渗透测试吗?

不能。LLM 能对静态代码做第一轮检查抓住明显问题,渗透测试涉及执行上下文、系统交互、权限提升、运行时行为分析。LLM 在渗透测试之前有用,不是代替它。

向 Linux 内核这种开源项目发 LLM 生成的安全报告合理吗?

如果报告经过人工核实、描述的是真实漏洞,有价值。如果是不经人工整理的原始 LLM 输出直接发给已经满负荷的维护者,那就是带着真实人力成本的噪音。问题不在于 LLM 生成了它,而在于人类核实这个环节从链条里消失了。

在 CI/CD 里自动化 LLM 安全审查值不值?

要谨慎。逐 commit 审查 token 成本涨得很快。对于自动 CI,最佳做法是选择性触发:只审查涉及认证、密钥处理、输入校验的文件,而不是每次 push 都全量审查 diff。

我的真实想法

LLM 作为第一层审查确实有用,比不审强。它在我代码里发现了三个真实问题,都是我一直拖着没改的。

但我不买账的是"LLM 审查 = 足够"这个叙事,更不买账"等同于专家人工审查"。这个叙事存在是因为它方便——对供应商方便,对有 deadline 的团队方便,对任何想要"有安全流程"这个感觉但不想真花成本做好的人方便。

模型漏掉的时序攻击并不算冷门,是有文档记录的已知模式,一行代码就能修。它们漏掉是因为它隐含在行为里,而不是显式在语法里。

诚实的权衡是这样的:LLM 安全报告给你可见内容的覆盖范围。不可见的依然不可见——而且现在它带了个"已审查"的戳。

这个戳——这才是让我不舒服的地方,也是我为什么要自己做这个实验,而不是坐在那儿接受第一轮让人安心的审查结果。

如果你做的东西有真实攻击面——公共端点、令牌处理、用户数据——别让 LLM 安全报告成为终点,当起点用。差异比看起来大得多。

原文:https://juanchi.dev/en/blog/llms-generating-security-reports-ran-prompt-on-my-own-code