让 AI Agent 访问真实数据库听起来很简单——给它一个只读用户就行了,对吧?

QueryBear 工程师在实战中发现,这个”简单”的方案只维持了两天。

问题一:模型会写它不该写的东西

模型完全有能力写出 DELETE 语句,然后用注释 tricks 绕过糟糕的正则检查。“只读”在应用代码层强制执行,离被一条错误的字符串匹配击败只有一步之遥。

问题二:模型会写”合法”但会摧毁数据库的东西

一个”只读” Postgres 用户仍然可以执行 SELECT pg_sleep(3600) 并悄悄占用一个连接。几次下来,你的连接池就死了,你的真实应用开始对所有人返回 500。

或者 Agent 写了一个跨 12 张表的笛卡尔积,返回 5 亿行——技术上完全合法,但会让你的数据库 OOM。

问题三:模型会读它不该读的东西

一个看起来无害的 users 和 oauth_tokens 的 join,可以拉出你根本没打算暴露的凭证。SQL 有效,用户有权限,结果是一场安全事故。

这个问题没有语法检查能catch到。

问题四:来自你自有数据的提示注入

这是最让人头疼的。你的 support_messages 表中的一行文本,可能说服 Agent 去查询一个完全不同的连接。

攻击者不是你的用户,而是三个月前某人写的一段文字,现在正躺在你的数据库里。

深层问题:每层防御都能被绕过

任何单一检查都是”纸墙”。你需要堆叠多层防御:

第一层:什么都不工作

从尽可能限制开始。默认是:没有表,没有列,没有写操作,什么都没有。

然后精确地揭开 Agent 实际需要的那些能力,一层不多。

第二层:每种失败模式加一层

解析器是一层。数据库级只读事务是一层。语句超时、行限制、列白名单、预执行成本估算器、审计日志。

每一层都存在是因为它前面的层可能失败。

第三层:像有人试图打破它一样测试

不是”happy path 通过”。是对抗性测试用例。从真实世界列表中提取的提示注入 payload。多语句攻击。看起来没问题但实际有问题的查询。看起来有问题但实际没问题的查询。

QueryBear 的实战方案

他们的技术栈包括:

  • SQL 解析器:严格只允许预期内的操作
  • 表和列白名单:Agent 只能看到它被允许看到的
  • AST 层重写:行限制和超时不能被 Agent 绕过
  • 预执行成本检查:拒绝会扫描过多的查询
  • 数据库级只读事务和语句超时:作为硬性兜底
  • 完整审计日志:可以重放所有操作

总结

给 AI Agent 数据库访问不是加一个”只读用户”就能解决的。你需要:

  1. 默认拒绝:从最限制开始
  2. 洋葱模型:每层防御都是下一层的后备
  3. 持续测试:模拟攻击者思维
  4. 审计追踪:出问题能追溯

如果你只建了一层,你建的是一堵墙。如果你把各层都建了,你建的是一栋楼。

相关资源