← 所有文章
·vibe-codingsecurityidorapi-testing

vibe coding 出来的应用,默认就自带安全漏洞

这是「vibe coding 会留下什么后患」系列的第三篇。第一篇讲真正能证明功能可用的测试,第二篇讲为什么这些应用总是坏。这一篇讲的是靠点点点根本看不出来的那种 bug:AI 从来没写过的那句授权校验。

AI 没写的那句校验

让 AI 造「一个发票页面」,你会得到一个路由:它加载 /invoices/42,取出这张发票,然后渲染出来。能用。你点一遍,数字都出来了,就发布了。

AI 要优化的目标,是把发票 42 渲染出来。它没有任何理由去问一句:当前登录的这个人,有没有权限看发票 42。于是服务器把匹配这个 ID 的那行数据原样递了回来。把 URL 改成 /invoices/43,你就在读别人的发票了。这就是 IDOR,即不安全的直接对象引用(insecure direct object reference),web 上最常见的一类越权访问。

AI 没写这句校验,是因为 prompt 里没有一个字要求它写。「让发票页面能用」这个目标,在页面对你自己能用的那一刻就达成了。缺的那一行说的是:这张发票得属于这个账户,否则返回 403。吃过亏的人类审查者会条件反射地补上它,而一个只管把 happy path 造出来的 AI 不会。

为什么你的端到端测试会漏掉它

你那些靠点击驱动的测试,是照着用户的路子走应用的。它们登录、落到 dashboard、打开自己的发票。UI 里的链接指向的都是它们自己的 ID,测试跟着点下去,通过了,全绿。

漏洞藏在一个前端从来不会渲染的 URL 上。你以发票 42 的所有者身份登录时,没有任何一个按钮会链到发票 43。你的测试框架只认识应用主动展示给它的那些 URL,它永远不会在地址栏里敲进一个陌生人的 ID,因为没有任何用户流程会这么干。对一个只会点击的测试来说,这个 bug 是隐形的。

要找出 IDOR,就得做一件 UI 死活不肯做的事:拿一个应用已经发出去的请求,改掉其中一个 ID,再以另一个用户的身份重发一遍。这是服务器信不信任你的问题,它藏在按钮底下。

拿变异过的 ID 重放真实请求

Hover 是一个免费、开源的 MCP server,加进你已经在用的编码 agent 就行,而越权访问测试正是为这种场景生的。它在你的开发浏览器前面架一个本地的 HTTPS 中间人代理,这样 agent 就能看到你的应用发出的真实 API 调用,连 header 带 body 一并看到。

接下来,agent 会做一件你的测试 suite 做不到的事:拿一个抓到的请求,把其中的对象 ID 变异掉,然后重放。如果服务器把发票 43 返回给了拥有发票 42 的那个账户,那就是一个已确认的越权访问漏洞,而不是猜测。agent 亲眼看到了真实的调用,改了一个值,然后眼睁睁看着服务器把数据泄了出来。

一个已确认的漏洞不会只停在一条聊天消息里。Hover 把它 crystallize 成一个 .api-test.spec.ts 回归门禁——一个普通的 spec,断言那个变异后的请求会被拒绝。这个门禁在 CI 里跑,回路里没有 agent。你一旦补上缺失的校验,门禁就一直保持绿色;日后要是有人又把这个漏洞引了回来,门禁就会在 pull request 上变红。agent 找到它一次,此后就交给 spec 一直守着。

同一个 agent,不用另装工具

越权访问测试,就跑在你本来就用来写测试的那个 agent 里。不用另装工具,没有 bundler 插件,也不用开第二个账户。从写一条功能测试,到对着同一个开发浏览器探测越权访问,一气呵成——每一个已确认的漏洞都会落成一个 .api-test.spec.ts 门禁,在 CI 里一直跑下去,比找到它的那次会话活得久得多。

这把你带到了哪一步

vibe coding 把成本挪了个位置。写功能现在很便宜,可要知道它安不安全,仍然是个活儿——而那个造出 happy path 的 AI,恰恰是你最不该指望它去找出自己跳过的那个漏洞的对象。解法是让一个 agent 去做 UI 藏起来的那部分:重放真实流量,改掉 ID,把它找到的东西变成一个比这次会话活得更久的门禁。

在你自己的应用上试试 Hover。

把 Hover 的 MCP 加到你已经在用的编码 agent。它探索你的应用,结晶出你自己拥有的纯 Playwright spec。

npm i -g @hover-dev/mcp && claude mcp add hover -- hover-mcp阅读快速开始 →