2021 年,纽约大学的研究者干了一件让程序员后背发凉的事:他们给 GitHub Copilot 出了 89 道编程题,让这个 "AI 结对程序员" 独立完成,然后把生成的 1,689 段代码逐行审计。消息一出,开发者社区炸了——其中约 40% 的代码存在可被直接利用的安全漏洞:SQL 注入、跨站脚本、缓冲区溢出,全是攻击者做梦都想遇到的那几样。

看到这个数字那天,我正把 Copilot 当结对程序员用。它补全什么,我基本就收什么,最多看一眼语法。40% 意味着什么?意味着我每写两段代码,就有一段可能带着一个能被打穿的洞。后来我静下心把论文读了一遍,又追了好几篇后续研究,才搞清楚这 40% 到底怎么算出来的、它适不适用于你我的日常、以及真正危险的东西其实藏在另一个地方。
40% 是怎么测出来的
纽约大学那篇论文的标题叫《Asleep at the Keyboard?》——"在键盘前睡着了?"。这个标题一语双关:既调侃程序员对 AI 输出不设防,也在调侃这位"结对程序员"其实是个不知疲倦、也毫无安全意识的输出机器。
研究者设计了 89 个安全敏感场景,覆盖 C、C++、Java、Python、JavaScript 五种语言:拼接 SQL 查询、生成 HTML、读写文件、解析网络请求。为了排除运气成分,每个场景还用不同措辞的提示词反复生成,最后凑齐 1,689 段程序,先用静态分析工具初筛,再由安全专家人工复核。
结论:40% 的程序被判定为存在可被利用的漏洞。不是代码风格问题,是攻击者真的能打穿。
| 漏洞类型 | 典型触发场景 | 后果 |
|---|---|---|
| CWE-89 SQL 注入 | 把用户输入拼接进 SQL 查询 | 攻击者直接读写数据库 |
| CWE-79 跨站脚本 | 把用户输入拼进 HTML 返回浏览器 | 在受害者浏览器里执行任意脚本 |
| CWE-787/119 缓冲区溢出 | C/C++ 不检查长度就复制内存 | 远程代码执行,直接拿下控制流 |
| CWE-798 硬编码凭据 | 把密钥、口令写死在代码里 | 泄露一次等于全盘暴露 |
它不在写代码,它在猜下一个词
看到 40% 时,我的第一反应是:训练数据不是几十亿行开源代码吗?开源代码再差,也不至于五分之二都是漏洞吧。
这个直觉错了一半。Copilot 学到的不是"人类知识的精华",而是 GitHub 公开仓库里真实存在的代码。这些代码的平均安全水平,就是整个开源世界的平均水平,而真实世界到处是漏洞:论坛流传的解答、无人维护的老项目、随手粘贴的片段,全进了训练语料。模型学到的是大多数程序员怎么写,不是正确安全的写法。把平均值学到极致的东西,写出来的当然就是平均值水平的代码。
另一半更根本——它压根不是在写代码,它是在做 token 预测。给它一段提示,它就根据前面出现过的所有 token,猜下一个 token 最该是什么。’SELECT * FROM users WHERE name = ‘ 后面跟一个直接拼接的字符串变量,在训练语料里出现太多次了,于是它照写不误。它不会想到这段代码会被注入攻击——注入发生在代码运行之后,而它活到写完最后一个字符就结束了。
还有一层:安全问题从来不是词汇搭配问题。同一句查询,参数化写法与非参数化写法在字符层面长得差不多,真正的区别是"用户输入能不能改变这条 SQL 的语义",这要求站在攻击者视角做推理。模型没有攻击者视角,它只有概率视角。
比 40% 更危险的是你的自信
如果只是漏洞率高,多检查总能兜住。但 2023 年斯坦福一篇论文让我意识到,事情比"多检查"更麻烦。
那项研究叫《使用 AI 助手的开发者会写出更不安全的代码吗?》,47 名参与者被随机分成两组,一组允许用当时性能最强的 AI 助手,另一组纯手写,任务是完成几个安全敏感的代码函数。结果:有 AI 助手的那组写出的代码漏洞更多,但他们给自己代码安全性的打分,反而更高。
这个组合很气人:写得更差,还更自信。原因不难猜——AI 生成的代码太像样了:格式规整、注释周全、函数命名老练。人脑有个偷懒机制:看到一气呵成的文本,默认它是真的。同事写出乱糟糟的代码你会逐行审,Copilot 吐出教科书一样干净的片段,你的防线自己先撤了。
论文的结论很扎心:使用 AI 助手的参与者写出了显著更不安全的代码,却更倾向于相信自己的代码是安全的。——Neil Perry 等,Do Users Write More Insecure Code with AI Assistants?
这才是 AI 编程时代真正的风险:40% 是可统计的漏洞率,过度自信是无法统计的放大器。
40% 不能直接搬到你项目里
但话说回来,你要是把这 40% 当作 Copilot 在你项目里的真实出洞率,就把结论用过头了。
纽约大学团队在论文里也承认,他们的场景是"孤立提示词"——不给任何上下文,让模型凭空写完整程序。真实开发完全不是这样:Copilot 能看到你的代码库、函数签名、注释和测试,这些上下文会显著改变输出质量。
后续研究也印证了这一点:在提示词里写明安全约束("使用参数化查询""所有输入先校验再使用"),或者让模型扮演"安全审计专家"再生成,漏洞率会有明显下降。纽约大学团队的另一项后续工作还发现,把模型生成的漏洞代码再喂回给它,让它"自己修一下",有一部分简单漏洞真的能被修掉。
还有一个很多人没提的参照系:人类写代码的漏洞率本来就不低。安全厂商每年的存量代码扫描报告都显示,半数以上被测应用带着至少一个已知漏洞。Copilot 只是把开源世界的平均水平复刻得惟妙惟肖。它不比人类平均差——但这恰恰是它最尴尬的地方:你期待它超越平均水平,它的设计却决定它只能逼近平均水平。
这两年,什么变了,什么没变
情况确实在变好。模型换代后,多个团队做过复测,SQL 注入这类教科书级别漏洞的生成比例明显下降。GitHub 也把这事当正经事在管:给 Copilot 集成了基于 CodeQL 的漏洞检测,2024 年推出了 Copilot Autofix,扫描到漏洞后自动生成修复补丁。把 40% 压到更低的两位数,完全可能。
但治标不治本。只要训练语料还是人类写的代码,而人类代码还带着漏洞,模型的概率分布里就永远有那块阴影;只要生成机制还是"猜下一个 token",安全就永远不是它的优化目标。你可以把最坏情况的概率压低,但"会生成带漏洞代码"这件事本身,是架构级的属性,不是修一个版本就能除掉的。
我的规则:把它当实习生,别当专家
所以我的结论不是"别用 Copilot",而是重新校正对它的定位。Copilot 的价值是见识广——五秒钟调出一个冷门 API 的标准用法,省掉大量查文档的时间。但它对安全的理解上限,就是开源世界的平均水平。正确的打开方式是:
- 默认不信任,审查走机制。凡是要进主干代码的 AI 生成片段,强制过一遍安全扫描(CodeQL、Semgrep、Snyk 都行),不要靠肉眼扫一眼。
- 把安全要求写进提示词。让它写数据库调用时注明"只准用参数化查询";处理用户输入时注明"按不可信输入处理"。它不会主动想到这些,但它极其擅长照做。
- 用攻击者的视角反向审。拿到 AI 代码先别急着夸,问一句:如果我是攻击者,这段代码哪里最疼?对照 OWASP Top 10 过一遍:注入、认证失效、敏感数据暴露、XML 外部实体……一次两分钟。
- 警惕"流畅"的陷阱。AI 生成的代码越干净、越顺眼,越要给自己提个醒:流畅不等于正确,更不等于安全。斯坦福那组参与者翻车,翻就翻在这里。
回到标题的问题:Copilot 生成的代码有多少包含安全漏洞?
答案分三层:实验室极端条件下,约四成;真实使用中,没有统一数字,但所有研究都指向"不低";而整件事里最确定的一点,不是模型的漏洞率,而是你放下警惕的那一秒。
我现在养成了一个习惯:每次 Copilot 补全涉及用户输入、数据库、文件、网络的代码,提交前都问自己一句——如果这是实习生写的,我会直接合并吗?答案几乎总是:不会。这个"不会",就是我和 AI 结对编程的安全边界。
进阶阅读:三篇值得亲自翻的论文
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/2144.html