有一次,我给一个想用 AI 查生产数据的团队做技术评审。他们很自豪地告诉我,已经给 Agent 开了一个只读账号,权限控制做得很好。我问了一个问题:这个 Agent 能触达多少张表?回答是:全库所有表。我又问:它查一张表的时候,数据库会不会死?他们愣住了。

后来他们测试发现,Agent 生成的一个 SELECT * FROM orders CROSS JOIN products 查询,差点把整个集群搞挂。你没听错,只读账号也可以制造灾难。所以,“只读”绝不等于“安全”。要理解 Agent 操作数据库的安全设计,你得从权限控制的粒度说起。
先搞清楚数据库自己能把权限管到多细
数据库的权限模型,本质上是一个多维矩阵:你能对哪个对象、做哪个操作、看到哪一行。
- 操作维度:SELECT、INSERT、UPDATE、DELETE、TRUNCATE
- 对象维度:一个库、一张表、一个列,甚至一行数据
- 动态维度:只有在满足 WHERE 条件下才允许操作
以 PostgreSQL 为例,你可以这样授权:
-- 只允许读一张表
GRANT SELECT ON sales TO agent_app;
-- 只允许读几列,其他列不开放
GRANT SELECT (id, product_name, sale_date) ON sales TO agent_app;
-- 定义视图,代理只认视图
CREATE VIEW sales_recent AS
SELECT id, product_name, price FROM sales
WHERE sale_date > now() - interval '30 days';
GRANT SELECT ON sales_recent TO agent_app;
-- 行级安全,让代理只能看到自己的数据
ALTER TABLE sales ENABLE ROW LEVEL SECURITY;
CREATE POLICY sales_org_policy ON sales
USING (org_id = current_setting('app.org_id'));
这段代码里,最后两条尤其关键。视图能控制列,行级安全能控制行。真正细粒度的权限,是可以做到“只让 Agent 知道它该知道的那些格子”的。
但现实里,大多数团队根本没用上这些,因为它们嫌麻烦。于是给 Agent 一个通配符权限,GRANT SELECT ON ALL TABLES,一步到位。危险就在这一步。
只读查询的三座暗礁
查询本身可以伪装成“只读但暴力”
一个 SELECT 语句如果做了 CROSS JOIN,可能返回几百亿行。数据库需要内存、磁盘和 CPU 来执行排序、分组、临时表操作。很多 DBA 看到慢查询之后第一反应是加索引,但 Agent 生成的查询往往不走寻常路,它可能会写出你从没预料的 JOIN 顺序,然后把资源耗尽。只读权限阻止不了资源消耗,只限制了数据的“改变”,却没有限制数据的“爆炸”。
只读不等于不泄露
如果 Agent 能够筛选任意列,它可以通过提示注入或用户诱导,把敏感列捞出来。更隐蔽的是盲注式的聚合查询:比如逐个测试字符,用 YES/NO 的回答来推断某列的值。只读权限完全允许这种“盘问式”查询。而且,Agent 可能把查询结果写进日志,日志再被第三方同步,信息就流出去了。
数据库的“只读”边界并非铁板一块
很多数据库的只读模式只禁止数据改动,但允许创建临时表、执行会写入磁盘的操作。在某些 DB 里,只读用户还能调用带副作用的函数,或者使用链接服务器访问外部数据源。只要权限配得不干净,只读账号也能变出各种各样的“动作”来。
读写操作:风险等级完全不同
只读的风险是“知道太多”和“消耗太大”,而读写操作的风险是“不可逆的破坏”。给 Agent 一个 UPDATE 权限,等于把一支上了膛的手枪放在一台概率机器上。
经典的场景:用户让 Agent 把“所有已经完成订单的金额加个 10% 折扣”,但 Agent 的 WHERE 写错,或者漏了 WHERE,结果全表被更新。如果这是生产库,你根本找不到一个“撤销键”。
所以,读写操作需要额外加一道至少三层的防线:
- 强制预检:Agent 生成写语句后,先在一个只读副本上执行 EXPLAIN,估算影响行数,超过阈值直接拒绝。
- 人工审批:写操作必须推送到一个待办队列,让真人确认。不要走自动提交。
- 事务熔断:要求所有写操作运行在事务里,并且设置 1 秒超时自动回滚。
如果你连这三层都做不到,那就老老实实把 Agent 的写权限关掉。没有任何业务紧急到需要一个幻觉模型直接改生产数据。
一张表说清只读查询和读写操作的安全差异
| 维度 | 只读查询 | 读写操作 |
|---|---|---|
| 核心风险 | 数据泄露、资源耗尽、信息推断 | 数据损坏、删除、提权 |
| 主要防护 | 视图、RLS、查询超时、结果脱敏 | 审批、事务回滚、变更审计 |
| 权限粒度要求 | 中(可限制对象、行、列) | 极高(最小权限+强制验证) |
| 是否建议 Agent 直接执行 | 在可控沙箱内可以 | 强烈不建议,必须人工介入 |
| 一个形象的比喻 | 让陌生人参观公司展厅 | 让陌生人进入档案室修改文件 |
真正的粒度:把 Agent 隔在数据库外面
前面讨论的都是“如果 Agent 必须写 SQL,怎么限制”。但我越来越觉得,这个前提本身就是问题。
AI Agent 的强项是想出方案,而不是执行一个精确的数据库操作。与其让 Agent 去生成 SQL,不如对它做一次抽象:只暴露几个函数接口,例如 query_sales_recent(days)、count_customers(filter),这些接口内部由人工代码去执行 SQL。Agent 的函数调用被记录下来,且无法越界。
这其实就是权限控制的最终粒度——不信任模型的语法生成能力,只信任它调用预设工具的行为。
在这种设计下,即使 Agent 被越狱,它也只会用你给它的那几个函数。你以为你在做权限控制,实际上你在做能力隔离。
我自己踩过的一个坑
文章写到这儿,我想起一个真实事故。之前我也觉得只读权限够安全,于是给 Agent 分配了一个只读角色,然后让它去介绍项目的数据库架构。结果 Agent 反复尝试 ALTER TABLE 语句,因为它在训练数据里知道如何加注释,但忘了自己的账号没有这个权限。每试一次,数据库就产生一条错误日志。它试了整整五分钟,最后把日志文件所在的磁盘填满了。
那一刻我才彻底明白:权限系统防的是“数据修改”,但防不住 Agent 的“行为失控”。真正的数据库安全,必须包含对 Agent 行为的约束——重试次数、并行度、连接数、错误率,这些通常不在权限系统里,但比权限系统更能救你的命。
结论:权限控制的粒度要匹配你的风险模型
最后,给出我的判断。对于 Agent 操作数据库的安全设计,权限控制的粒度并不是越细越好,而是要与场景风险匹配。
- 如果是给内部数据分析 Agent 用:默认只读,使用视图和 RLS 限制范围,加上查询超时和结果集上限,已经能管住大部分风险。
- 如果是给能产生写入行为的 Agent 用:必须把写操作隔离到人工审批通道里,并且每次执行前用静态分析和预执行校验。不要依赖 Agent 自己的判断。
- 无论哪种情况,都别忘了资源层面的护栏。一个只读 Agent 能把数据库搞挂,这本身就是最鲜活的教训。
你要做的不是控制模型,而是控制它可能造成的后果。权限粒度只是第一层,最后一层永远是人。
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/571.html