Vanna AI数据库查询安全:一条SQL从生成到落库,权限在哪几道关被检查
2026/9/5 15:20:14 网站建设 项目流程

Vanna AI数据库查询安全:一条SQL从生成到落库,权限在哪几道关被检查

【免费下载链接】vanna🤖 Chat with your SQL database 📊. Accurate Text-to-SQL Generation via LLMs using Agentic Retrieval 🔄.项目地址: https://gitcode.com/GitHub_Trending/va/vanna

一条"把客户列表发我"的自然语言提问,让模型生成的 SELECT 少了 WHERE 子句,三万行客户资料整表流出。AI 直连数据库后,应用层"只能看自己数据"的防线形同虚设。Vanna AI 的数据库查询安全设计,就是在请求链路上设下身份解析、权限校验、SQL 改写、审计落盘几道关。

🛡️ 一个请求进来,安全链路上发生了什么

Vanna AI 把"执行 SQL"建模成一次工具调用,安全逻辑全部挂在这条链上:

  • 身份解析:UserResolver从 Cookie、JWT 或会话头里取凭证,翻译成带group_membershipsUser对象;
  • 权限校验:工具的access_groups与用户所属组求交集,为空即拒绝,拒绝原因连同用户组信息记入访问检查事件;
  • SQL 改写:transform_args按用户上下文改写查询参数,行级过滤在这一步发生;
  • 执行与落盘:SQL 交给对应数据库的 runner 执行,工具调用、执行结果、AI 响应各自生成审计事件写盘。

校验的主体逻辑集中在src/security/,任何一步失败都不会走到下一步。

权限最终怎么落到某一行数据上

工具级权限决定"能不能执行",行级安全决定"执行后能看到哪些行"。SQL 先由检索增强加 LLM 生成,行过滤发生在生成之后、执行之前,这是整套权限模型的分界。

身份从哪来:请求被翻译成用户对象

UserResolver是个抽象基类,只要求实现一个方法resolve_user:从请求上下文的 header 或 cookie 里取凭证,返回UserUser上有两个关键字段:group_memberships决定用户属于哪些组,metadata携带组织、租户等上下文。认证可以是 JWT、SSO,也可以是聊天内验证邮箱的轻方案——框架只认"请求到 User"这个映射,不关心身份来自哪个系统。

权限组怎么定:工具声明谁能碰它

每个Tool都带access_groups属性。空列表表示所有已认证用户可用;声明了["finance_ops"],就只有组内成员能触发该工具。组的划分建议按数据域而非岗位命名:read_saleswrite_finance对应的是"能碰哪块数据",不是"什么职级"。这一步解决动作准入,数据行怎么过滤,交给参数改写。

数据行怎么被过滤:transform_args 里的多租户RLS

行级过滤落在ToolRegistry.transform_args:工具参数在执行前经过它,可以按用户改写,也可以返回ToolRejection直接终止。常见写法是给 SELECT 追加 WHERE——销售带自己客户 ID 的过滤条件,经理带团队组织 ID;SaaS 场景按organization_id逐租户过滤,就是标准的多租户RLS。查询里出现受限表名时返回拒绝而不是放行,过滤和拦截共用同一个钩子,少一条旁路。

🔍 事前、事中、事后各拦了什么

事前:生命周期钩子在消息进处理之前动手

LifecycleHook提供before_messagebefore_tool两个时机。前者在消息进入处理前运行,可以改写内容,也可以抛异常直接中止,配额检查、内容安全过滤都适合放这里;后者在每次工具执行前运行,抛异常即阻止这次调用。钩子以独立类注入,不侵入工具实现,检查逻辑可以单独测试、单独下线。

事中:限流与参数脱敏同步进行

工具执行期间,速率限制按用户或组计数,防止单账号把数据库刷爆;带注入特征的 SQL 在参数层拦截,不进入 runner。审计写入的参数默认脱敏,passwordtokenapi_key一类字段在写盘前替换为[REDACTED],避免敏感值随日志二次泄露。

事后:自然语言转SQL审计有据可查

AuditLogger定义了几类事件:访问检查(谁、哪些组、是否放行、拒绝原因)、工具调用(工具名、参数、是否脱敏)、工具结果(成败、耗时、返回大小)、AI 响应(响应哈希、调用的工具列表)。实现可写文件、入数据库或推 SIEM。事件带request_idconversation_id,一次提问的所有动作能串成时间线,按时间范围即可回溯。接口见src/audit/

上线前你需要改动的三处配置

把真实身份接进 UserResolver

不接真实身份,所有请求落到同一个匿名用户,权限组形同虚设:

class JwtUserResolver(UserResolver): async def resolve_user(self, request_context): token = request_context.get_header("Authorization") payload = decode_jwt(token) return User( id=payload["sub"], group_memberships=payload["groups"], )

在 transform_args 写行级过滤

没有这一步,有工具权限的用户仍能 SELECT 全表。组织 ID 建议放进用户metadata,由身份系统下发,不依赖用户自报:

class RlsRegistry(ToolRegistry): async def transform_args(self, tool, args, user, context): if tool.name == "execute_sql": args.query = append_where( args.query, f"org_id = {user.metadata['org_id']}" ) return args

打开审计并导出到日志平台

审计器不注入,事件就不落盘,事后追溯无从谈起。生产环境建议同时保留 AI 响应哈希,用于核对模型当时生成的 SQL 原文。其余配置项见docs/security.md

同一套机制在不同业务里的取舍

机制相同,参数不同。三个典型业务在粒度、审计深度和合规约束上的差别:

业务权限粒度审计深度合规约束
金融工具级 access_groups 细分到报表、账务域全量事件入 SIEM,长保留期,响应哈希留痕内审与监管的双重留痕要求
医疗以多租户 RLS 为主,按机构、科室过滤数据行脱敏默认开启,敏感字段不落盘HIPAA 患者隐私,访问可归因到个人
电商组较粗,靠配额与限流控制滥用抽样存储,异常模式告警GDPR 数据主体访问与删除权

取舍逻辑:金融宁可慢也要全量可追溯,审计不抽样;医疗的敏感点在数据本身,过滤前置、脱敏默认;电商并发高、单次查询价值低,预算花在限流上更划算。

回到开头那句"把客户列表发我":在 Vanna AI 的链路上,它会被定位到具体组织、在权限校验时被挡下、或在 SQL 改写时带上 WHERE 子句,并留下一条审计事件。整张客户表流出,正是这几道关共同拦下的场景。

【免费下载链接】vanna🤖 Chat with your SQL database 📊. Accurate Text-to-SQL Generation via LLMs using Agentic Retrieval 🔄.项目地址: https://gitcode.com/GitHub_Trending/va/vanna

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

立即咨询