☰
AI Agent数据探索权限沙箱:动态边界让效率与安全兼得
2026/10/8 16:18:51 网站建设 项目流程

AI Agent跑起来有多爽,数据安全那边就有多慌。我们团队在衡石平台上落地AI Agent数据探索能力时,业务方说的是"效率革命",安全团队说的是"你请了个精力旺盛的实习生,把公司数据库当练习场"。

两边都有道理。Agent自治性越强,探索越深入,它就越可能撞出安全边界。传统做法是干脆把权限收紧到只读,但那样Agent就成了一个"只能查固定报表的机器人",根本谈不上探索。真正的问题不是要不要给权限,而是怎么给、给到什么程度、边界画在哪里。

我们后来做的这套方案,就是衡石权限沙箱。核心思路一句话:把AI的探索能力关在一个透明的笼子里,它可以自由飞来飞去,但飞不出笼子。这篇文章从边界划分、动态授权、动作分级、技术实现到真实踩坑,完整讲一遍。

1. AI Agent接入数据平台后,传统权限为什么一夜之间失灵

1.1 传统数据权限的隐形假设

数据平台的传统权限模型,无论RBAC还是ABAC,底层都有一堆默认假设:操作者是"人",人有固定的角色,角色有稳定的数据集范围,人在登录后身份固定,能做的就是通过SQL或报表工具查询。

这套模型在人和BI工具的时代没有问题。人在查数据的时候,潜意识里会自我约束:我知道自己没权限看某些列,我不会去硬拼SQL;我知道某个字段涉及敏感信息,查到了也会注意。这些"礼貌"不是系统强制的,但系统默认人类操作者会遵守规则。

传统系统还有个特点:权限判定发生在"登录、授权、执行"这条链路的早期。人登录之后,后端给一套凭证,后续的查询基本信任这个凭证。会话期内的行为没有太多二次判定。这在安全上其实是有漏洞的,但人类操作者数量少、节奏慢,问题不至于集中爆发。

1.2 AI Agent带来的三个质变

Agent进来之后,三个变化直接击穿了传统模型的根基。

第一,意图不可控。Agent不是人,它不会"自觉"。给它一个目标,它会产生一连串推测、试探、验证行为。它可能先查A表看结构,再查B表试关联,再猜测某个字段的含义,然后写一个复杂的JOIN去验证自己的假设。这个过程本身就是发散性的,人脑判断不了它下一步要干什么。

第二,上下文超长。很多人低估了这一点。人在多轮取数对话里,会自己记住哪些权限有、哪些权限没有,不会再重复申请。Agent的多轮对话可能长达几十轮,它会不断重构自己的查询计划。如果权限判定只在第一轮做,后面几十轮就全是裸奔。

第三,目标漂移。Agent在探索过程中可能"发现"一些有趣的数据,然后脱离最初的任务目标,顺着这条新线索继续深挖。这在人身上叫好奇心,在AI身上叫越权路径。

我们现在用的Agent框架,有基于Python的,也有Rust实现的,底层差别很大,但共同点都是通过工具调用(tool calling)去操作外部系统。数据查询就是最常见的工具调用之一。你给Agent一个数据库连接器,它就能自主生成SQL,自主执行,自主解读结果。权限问题瞬间被放到显微镜下。

1.3 衡石权限沙箱的定位

我们当时内部讨论了很久,是做一个"Agent专用只读账号"——这是最省事的,所有Agent请求都走同一个低权限账号,查不到就报错。

但这个方案一测就死了。Agent面对权限报错时,不会像人一样停下来。它会换一种写法重试,换一张表再试,换一个函数再试,甚至把查询拆成多个小块绕过限制。一个只读账号挡不住这种强度的试探,反而会产生大量错误日志,把真正的问题淹没掉。

衡石权限沙箱的思路完全不同:不是给Agent一个固定账号,而是给每次Agent会话一个"身份令牌",令牌上绑定明确的边界描述。这个边界不是账号级的,而是查询级的。每次Agent发起查询,沙箱会重新计算边界、重新注入约束。也就是说,Agent虽然拥有自主探索的能力,但每走一步,脚下都有护栏。

2. 边界长什么样:行级、列级、查询级三层沙箱

2.1 行级边界:让Agent只能看到属于自己的那部分数据

行级安全(Row-Level Security)是权限沙箱的第一层。实现方式不复杂:在Agent生成的SQL后面绑定一组谓词条件,把它的视角限制在授权范围内。

比如销售数据表sales,登录用户是华东大区经理,Agent会话绑定他名下区域的数据,那么Agent不管怎么写SQL,最终执行前都会被注入:

WHERE region = '华东'

如果Agent写了:

SELECT customer_name, SUM(amount) FROM sales GROUP BY customer_name;

沙箱改写后变成:

SELECT customer_name, SUM(amount) FROM sales WHERE region = '华东' GROUP BY customer_name;

这层边界的关键不在"注入谓词"这个动作,而在"谓词从哪来"。衡石的做法是:数据模型层维护一张授权的维度映射表,记录每个身份标识(用户、角色、组织节点)能看到哪些维度值。Agent会话启动时,沙箱读取映射表,动态生成一组谓词片段,这个片段对Agent是不可见的,它只知道自己查到的是"合法数据"。

行级边界的另一个细节:AI Agent经常写子查询,不能只在最外层注入。比如Agent在子查询里统计某个城市的总量,外层再过滤一遍,如果只改外层,子查询里的全量数据命中依然会发生。所以改写要基于AST语法树,遍历所有表引用,逐表注入,而不是简单的字符串追加。

2.2 列级边界:敏感字段不是不让你看,是让你看脱敏后的

有些字段不能整列拒绝——比如订单表中的"金额"字段是分析必需,但"负责人手机号"就不是。直接拒绝会让Agent无法完成合理任务,放任又出事。折中方案是列级脱敏与字段白名单结合。

衡石在数据模型里对每个字段定义可见级别:

  • 完全可见:普通业务字段
  • 脱敏可见:手机号、身份证、邮箱等,用掩码函数包裹
  • 统计可见:只允许聚合函数触碰,不允许明细返回
  • 完全不可见:连字段名都不暴露

当Agent查询含脱敏字段时,沙箱自动改写列引用:

-- Agent原始写法 SELECT customer_phone, order_id, amount FROM orders; -- 沙箱改写后 SELECT mask_mobile(customer_phone) AS customer_phone, order_id, amount FROM orders;

有一类坑特别值得注意:Agent会通过SELECT *或者查询INFORMATION_SCHEMA来探测表结构,试图绕过列级白名单。我们当时在元数据层做了一层"字段名混淆"——给Agent展示的虚拟字段名和真实存储字段名不一致,沙箱在改写时完成映射。这个方案收益很大,Agent就算暴力枚举字段名,碰撞到的也是虚拟名,拿不到真实名,就没法构造针对敏感列的查询。

2.3 查询级边界:不能让它把数据库当自助餐

行和列管住了"看什么",查询级边界管"怎么查"。AI Agent在探索时可能产生大量重型查询,甚至无意中触发全表扫描、跨表笛卡尔积、超长窗口函数。这不只是数据泄露问题,还是资源占用问题。

查询级边界我们主要配了这几项:

边界项默认策略说明
单次查询最大返回行数5000行防止Agent拉全表
单次查询扫描行数上限100万行防止无过滤的全表扫描
单次查询运行时长30秒超时自动kill
数据源白名单已授权的项目/数据源防止Agent跨项目join
写入类操作全部拦截只允许查询,不允许写库
临时表操作池化隔离临时表只存在于沙箱内部,会话结束回收

查询级边界里最容易被忽略的是JOIN。Agent为了探索数据,经常会关联多张表,但授权往往是按单表配置的。A表有权限,B表没有,Agent写一个A JOIN B就同时拿到了两边的信息。查询级边界要求沙箱对所有涉及的表逐一核验权限,任何一张表越权,整个查询直接拒绝,而不是只保护A表。

3. 静态边界会困死探索,动态放行才是平衡术的关键

3.1 为什么静态边界必然失败

如果沙箱只是一个静态过滤器,AI Agent探索时马上会被卡死。它想查一张不在预设白名单里的表,查询被拦截,Agent会尝试换条件再查,又被拦截,几个来回之后,Agent就退化成只能查固定几张表的"填空机器人"了。

真正的数据探索一定包含"超出预期"的部分——Agent发现了某个关联关系,需要临时查看另外一张表的相关字段,这是探索价值所在。一刀切禁止,等于把探索消灭了。

所以衡石权限沙箱的第二层设计是动态放行机制。

3.2 授权令牌:每一次动态放行都有生命周期

设计核心是一个"临时授权令牌"机制。Agent发起一个需要额外权限的查询时,沙箱不直接拒绝,而是返回一个"授权请求"标记。Agent可以把查询意图(包括目标表、目标字段、筛选条件)一起提交给策略引擎。

策略引擎根据三件事做判断:本次会话的基础边界、请求的敏感等级、当前用户的历史行为画像。

如果判断属于低风险请求,策略引擎直接签发一个一次性授权令牌。令牌绑定以下信息:

  • 可访问的表和字段清单
  • 有效期(默认5分钟,探索类请求最长15分钟)
  • 查询扫描量上限
  • 允许的SQL模式(比如允许聚合,不允许明细导出)

Agent拿到令牌后,它的下一次查询会被令牌和基础边界叠加校验。令牌是一次性的,用完即焚。同一张表如果Agent要再次访问,必须重新发起授权请求。这个设计在初期开发时争议很大,有人觉得"用一次就失效太麻烦",但实际跑下来,AI Agent的探索路径是高度重复的,如果令牌不一次性,Agent会在第一轮把所有权限拿齐,后面全部绕过沙箱,那沙箱就名存实亡了。

3.3 二次确认与人工审批的接入

低风险放行进自动化流程,高风险放行必须进人工审批。

我们把审批流设计成"可嵌入Agent对话"的形态。Agent发起高危请求时,策略引擎会把这个请求推给数据Owner,Owner在审批页面能看到的不只是"谁申请了什么",还有"Agent的完整探索路径"——它前面分别查了哪些表、基于什么推断要查这张表。

这就是Agent可解释性的价值。人审批虽然慢,但能拦住完全背离任务目标的方向。比如有个Agent在分析用户留存,却申请访问薪资表,Owner一眼就能发现问题。这套机制上线后,人工审批通过率不到40%,大量看似合理的请求其实都偏离了任务主轴。

3.4 可撤销:会话结束、疑似越权、立即吊销

动态授权必须和会话生命周期强绑定。我们的实现方式是:每个Agent会话有唯一的沙箱标识,所有令牌都挂在这个标识下面。一旦会话结束,或者行为风控触发告警,整个标识下所有凭据全部吊销,已创建的临时表一并回收。

可撤销机制里有个容易被忽视的细节:Agent的异步任务。Agent经常"我现在查一个数据,结果出来后继续处理",这个继续处理可能在后台异步执行。如果异步任务不挂会话标识,它就会在会话结束后继续运行,带着已经失效的令牌访问数据。我们后续专门在任务调度队列里加了令牌快照校验,任务开始执行前还要再验一次权限,两次都通过才准跑。

4. 动作分级与策略映射:让Agent知道什么能碰、什么要申请

4.1 四级动作模型

AI Agent在数据平台上的所有行为,可以抽象成四个等级。等级划分的意义在于让Agent自身也建立起"边界感"——它不是被系统事后拦截,而是在规划阶段就选择合规路径。

  • L0 元数据级:浏览表名、字段名、统计信息、数据字典
  • L1 聚合级:count、sum、avg、group by、窗口函数聚合
  • L2 明细级:查看原始数据行,带脱敏
  • L3 物理导出级:把查询结果写到外部存储、生成数据文件、推送消息

L0是Agent探索的起点,它需要先了解数据长什么样才能决定下一步查询。默认放行,但要做频控——曾经出现过Agent在元数据层疯狂轮询表结构的行为,本质上是它在枚举可用数据集。

L1是真正的分析级操作,可以放行但必须限制资源。聚合查询计算量大,要配合扫描量上限和运行时间上限。

L2明细级是最敏感的一档。Agent返回原始数据行,哪怕是脱敏后的,也会产生真正的信息暴露。策略是:必须命中至少一个授权维度条件,且返回行数不超过500行,超过就必须走动态审批。

L3物理导出在任何场景下都需要人工审批,且审计日志永久保留。我们甚至不让Agent直接调用导出API,而是要求Agent提交"导出请求",由数据平台生成一条带审计标识的下载链接,链接有效期2小时,点击行为被记录。

4.2 策略表达:用策略模板约束Agent的探索路径

策略不能写在代码里,要配置化。我们参考了OPA(Open Policy Agent)的思路,把策略做成声明式规则集:

- action: L1_AGG_QUERY allowed: true conditions: scan_limit: 1000000 runtime_limit: 30s datasources: [dw_main, adhoc_analytics] - action: L2_DETAIL_QUERY allowed: conditional conditions: row_limit: 500 must_hit_scope: true mask_fields: [customer_phone, identity_no, email] fallback: request_approval - action: L3_EXPORT allowed: false fallback: request_approval audit: mandatory

这套策略的好处是安全团队可以独立更新规则,不需要改Agent代码。实际运行中,策略调整的频率比预期高很多——新的数据源接入、新角色上线、某个Agent出现异动,都需要快速调整。

4.3 行为风控:探索路径的异常识别

动作分级解决"单次行为合不合法",行为风控解决"一串行为合不合逻辑"。

AI Agent异常行为的典型特征是循环试探。正常Agent的查询路径是树形的,从一个主题发散出去,每个分支深度有限。异常Agent的路径是锯齿形的——同一张表反复查询但条件略微变化,连续多次尝试访问未被授权的高敏感表,单次查询返回行数突然暴增。

我们在沙箱日志上做统计窗口,三个核心指标:

  • 5分钟内同一Agent会话的查询次数上限(默认30次)
  • 10分钟内命中高敏感表的次数上限(默认3次)
  • 单会话累计扫描行数上限(默认200万行)

指标可以配置,但默认值不要调得太松。调太松的后果是Agent在不知不觉间完成了数据集的拼接探测——这次查A表一列,下次查B表一列,拼接起来就逼近真实信息。行为风控发现的是"组合路径",这是单次校验发现不了的。

4.4 会话级沙箱与请求级沙箱的区别

最后补一个设计决策点,这也是我们纠结最久的地方。

沙箱模式有两种:会话级和请求级。会话级指Agent整个对话生命期内,所有查询共享同一套边界和令牌。配置简单,系统压力小,但有状态漂移风险——上下文超长时权限状态可能和当前任务对不上。请求级指每次查询都要重新组装边界、重新校验,实现成本高,但状态无共享,最安全。

我们的方案是混合模式:基础边界使用会话级,动态放行的临时权限使用请求级。基础边界在会话创建时一次性绑定,稳定不变;而每次动态放行都是独立的一次性令牌,和会话状态无关,只跟具体这一次查询绑定。这既控制了性能开销,又把最危险的"临时权限超期使用"堵死了,建议参考这套思路的时候把这两个层级彻底分开,不要合并成一个"会话权限"去管理。

5. 实现沙箱不能只靠拦截,SQL改写与语境注入是硬功夫

5.1 SQL改写的核心原理与边界表达

沙箱拦截式防护在AI Agent场景下不靠谱,因为Agent天生会绕。所以真正的实现核心是SQL改写:让Agent永远感知不到"护栏"的存在,但它每次查询实际执行的SQL已经变了。

衡石的SQL改写基于语法解析,而不是字符串拼接。Agent生成的SQL先被解析成抽象语法树,语法树上遍历出所有表引用、列引用、聚合函数、子查询和关联条件,然后逐节点注入边界约束。

这里有一个关键设计:边界条件注入的位置要足够深。以行级安全为例,如果只注入最外层WHERE,子查询里依然可能暴露全量数据。正确的做法是在语法树的每个表节点上都注入对应的行级谓词,这样无论Agent怎么写子查询、嵌套多深,每一层的数据入口都被过滤过。

5.2 绕过的常见手法与对抗

我们在红蓝对抗测试中总结出几类绕过手段,给大家排雷:

  • UNION拼接:Agent构造两个查询用UNION合并,如果只注入一侧,另一侧就泄露。应对:对UNION两侧分别注入。
  • 子查询别名逃逸:在子查询里给表起别名,外层引用别名,简单字符串改写找不到。应对:基于AST的别名解析。
  • BETWEEN/IN模糊匹配:不直接查region = '华东',而是查region NOT IN ('华北','华南','西南','西北'),逻辑等价但文本完全变形。应对:行级谓词注入必须和Agent查询条件做AND合并,而不是覆盖或替换。
  • 时间函数探测:通过日期字段的时间范围反推数据分布,判断自己是否被过滤。这个拦截不了,但可以通过审计日志监控异常的时间条件模式。
  • 函数包含:将目标字段包在自定义标量函数里,绕过列级匹配。应对:函数展开解析,剥掉任何包在敏感列外的函数层。

对抗绕过的意义不是把每个攻击都拦掉,而是提高被绕过的成本。Agent的试探逻辑本质上是收益评估,如果每条越权路径都需要大量尝试且大概率失败,它的目标规划就会转向安全路径。

5.3 结果集过滤兜底

无论SQL改写做得多严密,都存在一个终极兜底方案:结果集过滤。

在Agent查询的返回值离开数据源之前,沙箱对结果集做最后一层校验。逐行逐列检查是否存在未授权数据,对脱敏字段再执行一次掩码,对超级行数进行截断,对包含未授权维度值的行进行剔除。

结果集过滤不建议在所有查询中都开启,它有可观的性能损耗。我们的策略是:L0和L1查询只做采样检查,L2明细查询必须全量过滤,L3导出前全量过滤并输出合规报告。也就是说,越是数据暴露风险高的动作,兜底检查越严格。

5.4 临时表与物化视图的权限继承

Agent探索时经常要求"先算个中间结果,后面基于中间结果继续分析"。这就涉及到临时表和物化视图。安全坑在于:如果Agent在沙箱内创建了一张临时表,临时表里的数据是过滤后的;但如果Agent绕过沙箱直接创建一张物理表,物化后的数据就脱离了预测级管控,后续所有查询都绕过了行级过滤。

我们的规则是:沙箱内一切物化操作产出的新表,自动继承创建者的权限边界,包括行级谓词和列级脱敏规则。物理表落地后,权限继承元数据写入表本身,任何后续查询都要重新校验原边界。这条规则实现起来不复杂,但必须在存储层做,不能靠应用层自觉。

6. 上线半年踩过的三个大坑与最终效果

6.1 JOIN越权:谓词只注入主表,漏了关联表

第一次红蓝对抗测试,安全同事就发现了一个致命口子。Agent查询订单表JOIN客户表,订单表有行级过滤,客户表当时因为某种原因没有配置权限映射,结果整个查询执行时,客户表的全量数据通过JOIN间接暴露了。

根因是我们的SQL改写器在AST遍历时只给主表注入了行级谓词,关联表被忽略了。修复方案是把"逐表注入"做成强制规则——所有出现在FROM和JOIN中的表,必须逐一绑定授权谓词,任何一张表没有映射就直接拒绝查询。不要试图在改写器里猜"哪些表重要",所有表一视同仁。

6.2 多轮对话的状态漂移:权限快照被缓存

第二次事故发生在Agent框架升级之后。Agent开始缓存会话内的权限校验结果,第一轮查询校验通过,后续几轮直接复用同一份权限快照,不重新走沙箱。这个优化的本意是减少延迟,结果导致会话中用户任务目标变化后,Agent依然用旧快照访问数据。

我们的修复是双重的:Agent端彻底移除权限快照缓存,每次查询请求都必须携带当前会话的上下文身份令牌,衡石沙箱端对每个查询请求做全量重校验。性能损失换来了确定性的安全边界。后来实测,全量重校验的单查延迟约增加20-35毫秒,对Agent多轮探索这个量级完全可接受。

6.3 异步任务绕过:调度队列里没人管令牌

第三个坑最隐蔽。Agent的某个数据导出具象化为一个异步任务压入队列,Agent会返回"正在处理"的提示。问题在于任务调度器在异步执行时,压根没检验任务携带的授权令牌是否还有效,导致明明会话已中断,后台标注任务还是成功执行了。

修复分两层:任务入队时绑定会话标识和令牌快照,任务开始执行前重新校验沙箱边界;如果会话已经注销或者令牌过期,任务直接进入失败分支,同时通知Agent发起方"授权已失效,请重新申请"。异步任务的安全校验不能依赖"入队时校验一次",执行前校验才是真正的生死线。

6.4 效果复盘

沙箱上线后,我们做了两轮验证。第一轮是红蓝对抗:内部安全团队伪装成攻击者,故意诱导Agent越权,所有攻击路径全部被沙箱拦截或触发告警,无一成功。第二轮是灰度观察:20个业务Agent并行跑了两个月,数据相关的安全事件清零,而Agent的查询成功率和探索深度都保持正常。

我个人最大的体会是:权限沙箱不是给AI Agent"戴上镣铐",而是给数据世界安装一套"交通规则"。规则存在的目的不是限制Agent跑,而是让所有Agent都知道——这条路可以走、这条路需要申请、这条路禁止通行。边界清晰了,自由才是可持续的。如果你正在给团队的AI Agent接入数据平台,我的建议是别先想着给Agent多大的权限,先想清楚你能接受的边界长什么样,边界画好了,放手让它探索,反而更安心。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询