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 项安全测试覆盖了哪些失败路径

2026-08-18 本地隔离执行:55 通过、0 失败、0 跳过;84 ms 为测试框架报告的用例时长,不含还原与构建。
本次使用独立 artifacts 与 results 目录运行 NL2SqlSecurityPolicyTests,没有停止工作区既有服务。测试覆盖:缺少服务端授权标记时失败关闭、两套 JSON 序列化器都无法伪造可信字段、Schema 结果按精确白名单过滤、JOIN 与子查询逐表检查、危险语法拒绝,以及不同数据库的行数封顶。
证据边界| 55/55 证明当前本地安全策略测试通过;它不等同于生产租户权限配置、线上数据库连接或公开接口已经完成验收。
✦ ⑦ 一份可复用的落地检查表
- 从服务端会话恢复租户与用户,拒绝客户端自报身份。
- 先求租户、角色、表权限、行级能力与请求范围的交集。
- 只检索并披露交集内 Schema,使用精确表名匹配。
- 把授权标记与行数上限设计成不可反序列化的服务端字段。
- 对模型 SQL 做第二次数据源解析、只读检查与方言级行数封顶。
- 分别记录源码测试、运行环境与生产验收,不把其中一层替代另一层。
Microi吾码AI 官方文档展示了“问题理解—Schema 检索—SQL 安全验证”的 NL2SQL 流程;本文进一步强调其中的授权时序。真正可靠的 AI 查库,不是让模型更大胆地猜,而是让它在更小、更可信的世界里工作。
资料| 官方文档:https://www.microi.net/doc/system-engine/ai-engine.html;源码与测试复核时间:2026-08-18。
更多推荐




所有评论(0)