RAG 如何做权限控制?
简化版
RAG 权限必须在检索前和上下文组装前由后端基于真实身份强制执行,不能靠 Prompt 要求模型保密。索引中的每个 chunk 继承租户、文档 ACL/安全域和权限版本;查询做租户隔离与 pre-filter,候选再实时 ACL 复查,只有授权文本能进入 LLM。缓存、引用、日志和权限变更也必须遵守同一边界。
详细版
认证确定用户是谁,授权决定能对哪些 document 执行 read。粗粒度可按 tenant/space 分区,细粒度用用户组、ABAC 或 policy_id;权限过滤条件由服务端生成,模型与用户都不能修改。向量库无法高效复杂过滤时,可扩大授权分区内候选并 post-filter,但不能先把无权内容发给模型再遮答案。
撤权用版本化事件和 deny/tombstone 秒级生效,主动失效检索、上下文和答案缓存;引用点击再次鉴权,日志最小化。评测包括跨租户查询、同主题无权文档、缓存复用、旧会话、索引迁移和时间差攻击,核心硬指标是无权 chunk 进入上下文次数必须为零。
身份令牌 -> 授权服务 -> tenant/security-domain pre-filter -> ANN
-> candidate ACL post-check -> LLM context
完整版教学
一、权限控制发生在生成之前
如果无权文档已经进入 LLM 上下文,即使回答最终没显示,数据也可能被外部模型处理、日志记录或后续轮次泄露。输出过滤太晚。安全目标是未经授权的 chunk 从不越过上下文边界。
Prompt 中“不要泄露”只能帮助模型行为,不能证明隔离。授权必须由确定性服务根据当前认证主体和资源策略判定,失败时 fail closed。
二、认证、授权和检索要分清
认证得到 user_id、tenant 和可信 claims;授权计算 can_read(user, document, context);检索只在授权集合中排名。用户问题里的“我是管理员”不影响真实身份,模型生成的 filter 也不能增加权限。
allow = authenticated
&& same_tenant
&& policy_engine.can_read(subject, document)
&& document.status == active
记忆钩子:检索分数决定“相关不相关”,授权策略决定“能看不能看”;相关性永远不能补偿权限不足。
三、多租户隔离有哪些方案
物理独立索引隔离强,适合高价值少量租户,但运维成本高;共享索引用 tenant_id 强过滤,规模效率高但配置错误影响大;分安全域的混合方式在二者之间。选择取决于威胁模型、租户数和合规。
| 模式 | 隔离 | 成本 | 风险 |
|---|---|---|---|
| 每租户独立索引 | 强 | 高 | 索引数量爆炸 |
| 共享索引+过滤 | 逻辑 | 低 | 漏 filter 即泄露 |
| 安全域分区+ACL | 中强 | 中 | 策略较复杂 |
共享模式在 SDK 层强制注入 tenant filter,业务调用方不能选择关闭。
四、细粒度 ACL 如何进入索引
每个 chunk 继承 document_id 和 acl_version,可带 allowed_groups 或 policy_id。组成员变化频繁时,不宜把所有 user_id 展开到每块;查询时将可信用户组映射成过滤,或由策略服务复查候选。文档移动文件夹也要触发 ACL 更新。
权限字段与内容字段分开版本化。内容 hash 不变不代表 ACL 不变。索引中缺权限元数据的文档默认不可见,不能默认 public。
五、Pre-filter 与 Post-check 缺一不可
Pre-filter 减少无权候选并防中间系统接触数据;但向量库实现、复杂组策略和索引陈旧可能产生漏洞。最终 context builder 对每个 document_id 调用当前授权或验证签名,形成第二道门。
Post-filter 后结果不足时,只能在授权空间扩大搜索,不能用无权结果补齐 Top-k。候选 ID 的日志和指标也可能泄露文档存在性,跨租户可见性需限制。
六、缓存与会话如何继承权限
最终答案可能概括私有文档,语义缓存不能跨权限共享。key 包含 tenant 与 permission fingerprint,命中时重新检查依赖文档 ACL。用户离组后,旧会话中的历史摘要也要清理或重新构造。
引用 URL 使用受控下载路由,每次点击鉴权,不能暴露对象存储永久链接。权限事件按 document tag 失效检索、context 和 answer 缓存;TTL 只作兜底。
七、如何测试零泄露目标
构造两个租户同名文档、同租户不同组、撤权后的缓存命中、索引 lag、旧会话和恶意 filter。观察每阶段候选,硬断言 unauthorized chunk 不进入 Prompt。最终答案不含秘密只是辅助指标,因为模型可能碰巧没复述。
例如 10000 次红队请求,输出泄露 0 次但有 3 次无权 chunk 被发送给模型,仍判失败。监控 unauthorized_context_count=0、ACL check latency、撤权传播 P99 和 fail-closed 次数。
八、常见误区与追问
- 误区:在 System Prompt 写“尊重权限”就够了。 模型不知道真实 ACL,也可能被注入绕过。
- 误区:最后答案脱敏就没有泄露。 无权正文进入 LLM 或日志已经越界。
- 误区:共享缓存只要查询相同就可复用。 答案依赖用户可见证据,必须权限隔离。
- 追问:向量库不支持 ACL 怎么办? 按租户/安全域分区,扩大授权域候选后实时 post-check,或更换能力。
- 追问:权限缺失字段如何处理? 默认拒绝并告警,不可按 public 处理。
- 追问:如何处理撤权延迟? deny overlay 先行、缓存主动失效、查询实时复查,物理清理异步。
九、加强记忆
RAG 权限可记成“身份后端定、索引先过滤、上下文再复查、缓存跟权限、撤权先阻断”。模型与用户不能生成授权,任何无权 chunk 都不进入 LLM;引用和旧会话同样再次鉴权。安全验收看中间上下文零越权,而不只看最终是否偶然没有泄露。