RAG 检索权限控制:先过滤证据,再让模型看到内容
只在生成阶段提示模型不要泄露信息不是权限控制。本文讲解 RAG 的身份上下文、ACL 过滤、缓存隔离、引用验证和越权测试,让模型从一开始就看不到不该看的文档。
本文要点
- 主题聚焦:AI、RAG、DevOps
- 适合人群:独立开发者、全栈工程师、AI 应用开发者
- 核心价值:快速理解背景、实践路径、常见误区和工程建议
“请只根据有权限的资料回答”可以改善模型行为,却不能成为安全边界。只要未经授权的 chunk 已经进入上下文,模型、日志、trace 和缓存就可能暴露它。RAG 权限设计的核心原则很简单:先根据可信身份过滤候选证据,再把允许的内容交给模型。
身份上下文不能来自用户输入
检索请求中的用户 ID、组织 ID、角色和项目成员关系必须来自已验证的会话或服务端授权上下文。不要让前端传入任意 tenantId 后直接拼进向量查询,也不要只在 prompt 中描述“当前用户属于 A 团队”。检索服务应接收一个不可伪造的 subject,内部再解析其可访问的资源集合或策略标签。
对于代理代表用户执行的场景,记录原始用户和代理应用两个身份。这样既能判断用户是否有业务权限,也能限制某个客户端能否调用高风险检索或导出接口。权限变化后应尽快失效会话和缓存,不能依赖用户下次登录才刷新。
ACL 过滤要参与召回,不是后处理
每个文档或 chunk 写入索引时带上最小可用 ACL 元数据,例如租户、项目、部门、文档可见级别和有效期。查询时把这些条件作为向量或全文检索的前置过滤。先取全局 top-k 再从结果里删无权 chunk,会造成两个问题:可用结果不足,且候选排序或日志可能泄露未授权资源。
如果权限模型复杂,不要把完整成员列表复制到每个 chunk。可以存稳定的 policy ID 或资源标签,在检索网关根据授权服务计算可用过滤条件。关键是过滤逻辑有单一实现和测试,不让每个业务工具各自拼一段 where 条件。
缓存和引用也属于权限面
检索缓存 key 至少包含主体或权限版本、租户、查询 hash 和索引版本。只按 query 缓存“退款流程”这类结果,会让后一个用户直接读到前一个用户的私有文档。权限变更时主动失效相关缓存;无法精确失效时宁可缩短 TTL,也不要延长泄露窗口。
模型输出中的引用链接同样要二次校验。某个 chunk 在回答时合法,不代表用户稍后点击链接仍有权限。引用服务在展示原文、下载附件或展开上下文时重新走授权,不把原始对象存储地址直接交给浏览器。
把越权尝试写成回归测试
测试集不只验证“有权用户能查到资料”,还要验证无权用户拿到文档 ID、相同 query、旧缓存 key、旧引用链接和异步任务 ID 后仍然失败。覆盖跨租户、成员被移除、文档到期、管理员支持模式和服务账号调用等场景。每次更换向量库、缓存层或权限服务,都重跑这组测试。
正确的 RAG 权限控制会让某些问题返回“我没有访问相关资料的权限”或要求用户选择项目。这个结果比编造答案更安全,也比泄露一段真实内容更容易修复。把授权放在检索之前,才能让模型只在允许的知识边界内发挥能力。
策略发布需要可观测的过渡期
权限规则变化时,先在 shadow 模式计算新旧策略的差异,记录会被新规则拒绝或新增允许的文档集合。对高影响差异由业务负责人确认,不要直接把新 policy 推到所有检索流量。正式切换后监控拒绝率、空结果率和跨租户拦截事件,既防止误放行,也防止正常用户突然失去资料。
服务账号和后台任务同样需要最小权限。索引 worker 可以读取源文档,不代表查询服务可以绕过用户 ACL;支持工具可以诊断问题,不代表它能把客户内容带进模型上下文。把不同职责拆成不同身份,权限设计才能在系统扩大后仍保持清晰。
上线前用真实权限矩阵做验收:不同角色、不同项目、已过期成员和支持人员分别执行相同 query,比较可见证据和引用链接。结果必须能够解释每一条允许或拒绝,而不是只检查最终回答有没有文本。授权系统能被测试和审计,才不会在新工具接入时悄悄失效。
当无法判断用户上下文时,默认拒绝并返回可行动的澄清提示。任何“为了不影响体验先给结果”的兜底路径,都会在多租户系统里变成真正的泄露风险。
把这一默认拒绝路径也纳入产品文案和支持流程。用户需要知道该如何重新选择项目、申请权限或联系资源 owner;支持人员则需要看到拒绝原因和 trace,而不是为了回答工单临时绕过 ACL。安全的体验不是从不拒绝,而是拒绝时仍然给出正确、可操作且不泄露信息的下一步。
延伸阅读
Related