打开网易新闻 查看精彩图片

去年夏天,一位安全工程师在审计某初创公司的代码库时,发现了一个诡异现象:全栈都是Cursor和Claude Code写的,代码整洁、测试全绿、ESLint零警告——但生产环境被人用' OR '1'='1一句话掏空了用户表。

问题出在一行看起来毫无破绽的模板字符串。

这不是什么上古PHP教程里的字符串拼接。这是2024年的"现代写法":反引号包裹的SQL查询,${filter}变量丝滑嵌入。代码能跑、测试能过、TypeScript不报错。直到有人把恶意输入塞进查询参数,数据库才突然敞开大门。

OWASP把这类漏洞编为CWE-89,在Top 10榜单上蹲了超过十年。它只是换了个马甲——从"SELECT ... '" + filter + "'"的丑陋拼接,变成了更隐蔽的模板字面量。视觉上干净了,危险一点没少。

为什么AI模型偏偏学坏了这门手艺

为什么AI模型偏偏学坏了这门手艺

答案藏在训练数据里。

大模型吞下了数百万代码示例,其中大量来自2014到2018年的教程、Stack Overflow高赞回答、以及从未被审计的GitHub仓库。那个年代,参数化查询还不是默认选项,字符串插值是"能跑就行"的快捷方式。

模型学到的规律很朴素:模板字面量确实"工作"。代码执行、测试通过、开发环境无异常。没有任何反馈机制告诉它——这种写法在生产环境等于给攻击者留后门。

打开网易新闻 查看精彩图片

更棘手的是结构性欺骗。Cursor生成的代码骨架往往完全正确:async/await用法规范、路由处理器形状标准、变量命名合理。只有查询构造那一行埋了雷,而雷被埋在语法糖里。

安全研究员在原文中贴出了典型生成样例。一个基础的/api/users过滤接口,db.query的第一个参数是反引号包裹的模板字符串,${filter}直接拼接用户输入。OWASP测试用例里的经典攻击载荷,在这里依然百试百灵。

修复只要10秒,前提是你知道该搜什么

修复只要10秒,前提是你知道该搜什么

参数化查询是所有现代数据库驱动的标配能力。pg驱动用$1占位符,mysql2用?,Sequelize或Prisma则直接上ORM查询构造器——整类问题随之消失。

关键改动只有一处:把查询字符串和用户数据拆成两个独立参数。驱动层自动处理转义,占位符不再是字符串插值的切入点。

原文作者的建议很具体:全局搜索db.query调用,筛选第一个参数包含反引号的实例。这个正则能在大多数代码库里瞬间定位风险点。

但知道该搜什么的人,47%不到。

我们团队内部做过一个非正式统计:让20位有3年以上经验的全栈工程师审查一段Cursor生成的CRUD代码,只有9人第一时间注意到模板字符串的注入风险。其余11人把重点放在了错误处理、类型定义和性能优化上——那些"更像问题"的地方。

打开网易新闻 查看精彩图片

这暴露了一个产品层面的盲区。Cursor的界面不会给这行代码标红,Copilot不会在这里停顿,ChatGPT的代码解释器也不会主动警告。安全缺陷被包裹在"现代语法"的糖衣里,完美融入了AI生成代码的置信区间。

当"能跑"成为唯一的验收标准

当"能跑"成为唯一的验收标准

模板字面量在JavaScript里是个优雅的设计。字符串插值让代码可读性飙升,直到它被用在了错误的位置。AI模型没有"位置感"——它只有概率分布。

一位在金融科技领域做代码审计的读者留言说,他们现在把"反引号SQL查询"列入了强制人工审查清单,无论代码来自人类还是AI。代价是审查时间增加了15%,但生产事故归零。

另一个团队的做法更激进:在CI流程里植入静态规则,任何包含模板字符串的原始SQL调用直接阻断构建。误伤率约3%,但可接受。

这些补丁都在解决同一个问题:当生成工具把"写代码"的门槛降到接近于零,"读代码"的能力反而成了稀缺资源。不是读语法——是读风险模式。

原文作者在最后抛出了一个未被回答的问题:如果训练数据里的坏模式无法被自动清洗,我们是否需要在每个AI代码生成器的输出层,强制插入一道针对OWASP Top 10的专项审查?

这道审查该由谁来做,做到什么粒度,误报和漏报的平衡点在哪里——你的团队是怎么处理的?