AI 查库权限交集与 Schema 最小披露流程封面

先确定谁能看什么,再让模型生成 SQL;顺序本身就是安全边界。

摘要| 把数据库结构整包交给模型,再对生成 SQL 做一次关键词过滤,看起来省事,实际上把越权风险提前送进了提示词。本文结合 Microi吾码AI 当前 NL2SQL 授权链路与 55 项本地安全测试,拆解一条更稳的顺序:身份可信、权限求交、Schema 最小披露、SQL 二次校验、结果行数封顶。

① 真正的第一道边界,不在 SQL 生成之后

很多 NL2SQL 方案把注意力放在“只允许 SELECT”“拦截 DROP”上。这些检查当然需要,但它们发生得太晚:如果模型一开始就看到了整个租户的表名、字段名和注释,未授权结构已经进入提示词,后面的 SQL 拦截无法收回这次披露。

更准确的问题应当是:当前登录用户在这个租户、这些角色和菜单权限下,究竟能让模型看到哪几张业务表?答案确定之前,不应开始 Schema 检索,更不应把客户端传来的 AllowedTables 当成授权事实。

顺序判断| 关键词扩展只负责提高召回率,不能扩大权限;客户端请求表只能缩小服务端白名单,不能把未授权表加回来。

从身份到执行的五步权限流程图

权限交集发生在 Schema 检索和提示词组装之前。

② 权限交集是一道逐层收窄的漏斗

当前实现先从 DiyToken 还原用户与租户,再取得租户业务表集合;控制面表不会混入普通业务候选。随后叠加角色侧的 AI 原始 SQL 策略、FormEngine 实际读取权限,以及调用方主动指定的表。每一层只做交集,最终为空就直接拒绝。

候选表 = 租户业务表
候选表 ∩= 角色 AI 策略表
候选表 ∩= FormEngine 可读取且无危险行级范围的表
候选表 ∩= 客户端请求表(若提供)
if 候选表为空:拒绝;否则才检索 Schema

这里有一个容易忽略的边界:某张表即使有读取权限,只要它带有通用 SQL 无法安全复现的数据范围,也不应进入通用 NL2SQL。宁可拒绝,也不要把“能打开表单”误写成“能绕过 FormEngine 直接查整表”。

兼容旧库| 没有显式 AI 策略时可以回退到 FormEngine 的真实读取权限,但仍要过滤无法安全复现行级范围的表,并设置可信的服务端行数上限。

③ 客户端可以提需求,不能给自己盖授权章

前端提交 AllowedTables 有合理用途,例如用户只想在订单与客户之间提问。但“服务端已经授权”“最多返回多少行”必须是只存在于后端对象中的字段。当前参数模型把这两个字段同时从 Newtonsoft.Json 与 System.Text.Json 序列化入口排除,授权服务每次还会先清空它们,再依据真实身份重算。

[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public bool ServerAuthorizationApplied { get; internal set; }

[Newtonsoft.Json.JsonIgnore]
[System.Text.Json.Serialization.JsonIgnore]
public int ServerMaxRows { get; internal set; }

这不是“隐藏字段”式安全,而是把信任来源固定为后端决策。即使请求 JSON 伪造同名属性,反序列化后也不能把它们写成可信状态。

提示词前授权与执行前校验双门禁矩阵

提示词最小披露和执行前 SQL 校验解决的是两类不同风险。

④ Schema 检索也必须服从白名单

权限交集得到的不是“建议表”,而是一份本次请求的服务端快照。向量召回或关键词检索返回 Schema 后,还要按精确表名再次过滤,避免相似名称、别名或检索噪声把无关表带进提示词。比如允许 order,不应因为子串匹配顺手放过 order_secret。

这一点让提示词具备最小知情原则:模型只看到完成当前问题所需、且当前用户确实能读的结构。召回模型可以决定“相关不相关”,但不能决定“允许不允许”。

缓存原则| 权限结果可以按租户、用户、候选表签名和授权版本短时缓存;授权版本变化后应自然换 Key,缓存异常则回到实时校验,而不是放行。

⑤ 模型生成 SQL 后,还要再过一次结构化门禁

最小 Schema 并不等于模型永远正确。生成结果仍需确认是单条 SELECT,拒绝注释、多语句、CTE、UNION、写操作、危险函数与变量表达式;解析每一个 FROM、JOIN 和子查询数据源,并用精确白名单逐个核对。逗号连接也应拒绝,避免数据源边界被模糊。

validate(sql):
  必须以 SELECT 开始且只有一条语句
  遍历所有 FROM / JOIN / 子查询来源
  每张物理表必须在服务端白名单中
  拒绝危险关键字、函数、变量和逗号连接
  按数据库方言外包一层限制,最多取 MaxRows + 1

多取 1 行不是放宽上限,而是为了判断结果是否被截断。可信上限被硬限制在 1—100 行,再按 MySQL、PostgreSQL、SQL Server、Oracle 等方言应用外层限制。这样即使模型自带 LIMIT 或 TOP,也不能突破服务端边界。

  • 提示词前:只披露授权交集内的 Schema。
  • 执行前:核对每个 FROM、JOIN 与子查询来源。
  • 返回前:按数据库方言封顶,并明确结果截断边界。

⑥ 真实验证:55 项安全测试覆盖了哪些失败路径

NL2SQL 安全策略 55 项测试全部通过的证据卡

2026-08-18 本地隔离执行:55 通过、0 失败、0 跳过;84 ms 为测试框架报告的用例时长,不含还原与构建。

本次使用独立 artifacts 与 results 目录运行 NL2SqlSecurityPolicyTests,没有停止工作区既有服务。测试覆盖:缺少服务端授权标记时失败关闭、两套 JSON 序列化器都无法伪造可信字段、Schema 结果按精确白名单过滤、JOIN 与子查询逐表检查、危险语法拒绝,以及不同数据库的行数封顶。

证据边界| 55/55 证明当前本地安全策略测试通过;它不等同于生产租户权限配置、线上数据库连接或公开接口已经完成验收。

⑦ 一份可复用的落地检查表

  1. 从服务端会话恢复租户与用户,拒绝客户端自报身份。
  2. 先求租户、角色、表权限、行级能力与请求范围的交集。
  3. 只检索并披露交集内 Schema,使用精确表名匹配。
  4. 把授权标记与行数上限设计成不可反序列化的服务端字段。
  5. 对模型 SQL 做第二次数据源解析、只读检查与方言级行数封顶。
  6. 分别记录源码测试、运行环境与生产验收,不把其中一层替代另一层。

Microi吾码AI 官方文档展示了“问题理解—Schema 检索—SQL 安全验证”的 NL2SQL 流程;本文进一步强调其中的授权时序。真正可靠的 AI 查库,不是让模型更大胆地猜,而是让它在更小、更可信的世界里工作。

资料| 官方文档:https://www.microi.net/doc/system-engine/ai-engine.html;源码与测试复核时间:2026-08-18。

Logo

宁波官方开源宣传和活动阵地,欢迎各位和我们共建开源生态体系!

更多推荐