17-AI应用防止越权访问知识库

AI 应用如何防止用户越权访问知识库

系列:从零构建企业 RAG 知识库(第 17 篇)

1. 越权不只发生在向量检索

攻击路径可能出现在:

  • 猜测文档 ID 直接下载原文;
  • 缓存复用其他用户答案;
  • 关键词索引有权限、向量索引没有;
  • 调试接口返回全库候选;
  • 历史会话保留撤权前资料;
  • 模型工具接受用户提交的租户 ID。

因此需要纵深防御,而不是一个过滤条件。

2. 可信身份与资源请求分离

python 复制代码
from dataclasses import dataclass


@dataclass(frozen=True)
class Identity:
    user_id: str
    tenant_id: str
    permission_version: str
    roles: frozenset[str]


@dataclass(frozen=True)
class KnowledgeRequest:
    question: str
    requested_document_ids: frozenset[str]

请求可以表达"想查哪些文档",但不能声明"我属于哪个租户"。

3. 交集而不是信任请求范围

python 复制代码
def effective_scope(
    request: KnowledgeRequest,
    authorized_document_ids: frozenset[str],
) -> frozenset[str]:
    if not request.question.strip():
        raise ValueError("问题不能为空")
    if request.requested_document_ids:
        return request.requested_document_ids & authorized_document_ids
    return authorized_document_ids

客户端请求未授权文档时,结果只能缩小,绝不能扩大。

4. 查询后再次验证结果

预过滤是主边界,结果复核是纵深防御:

python 复制代码
@dataclass(frozen=True)
class RetrievedChunk:
    chunk_id: str
    document_id: str
    tenant_id: str
    text: str


def enforce_result_scope(
    identity: Identity,
    chunks: list[RetrievedChunk],
    authorized_document_ids: frozenset[str],
) -> list[RetrievedChunk]:
    violations = [
        item.chunk_id for item in chunks
        if item.tenant_id != identity.tenant_id
        or item.document_id not in authorized_document_ids
    ]
    if violations:
        # 不能静默丢弃后继续回答,否则安全缺陷会被掩盖
        raise PermissionError(f"检索器返回越权结果:{violations}")
    return chunks

如果结果复核失败,应阻断请求并触发安全告警。

5. 权限感知缓存

python 复制代码
from hashlib import sha256


def answer_cache_key(
    identity: Identity,
    question: str,
    scope: frozenset[str],
    knowledge_version: str,
) -> str:
    raw = "|".join([
        identity.tenant_id,
        identity.user_id,
        identity.permission_version,
        knowledge_version,
        ",".join(sorted(scope)),
        question.strip(),
    ])
    return sha256(raw.encode("utf-8")).hexdigest()

用户撤权后 permission_version 改变,旧缓存不再命中。高敏内容可不缓存。

6. 原文下载使用短时授权

python 复制代码
from datetime import datetime, timezone


@dataclass(frozen=True)
class DownloadGrant:
    user_id: str
    document_id: str
    expires_at: datetime


def validate_download_grant(
    grant: DownloadGrant,
    identity: Identity,
    authorized_document_ids: frozenset[str],
    now: datetime,
) -> None:
    if grant.user_id != identity.user_id:
        raise PermissionError("下载授权不属于当前用户")
    if now >= grant.expires_at:
        raise PermissionError("下载授权已过期")
    if grant.document_id not in authorized_document_ids:
        raise PermissionError("当前已无文档访问权限")

生产授权还要有不可伪造签名或服务端状态,数据类本身不是完整令牌方案。

7. 可复验负向测试

python 复制代码
def test_requested_scope_can_only_shrink() -> None:
    request = KnowledgeRequest("查询制度", frozenset({"allowed", "secret"}))
    scope = effective_scope(request, frozenset({"allowed"}))
    assert scope == frozenset({"allowed"})


def test_cross_tenant_result_blocks_entire_answer() -> None:
    identity = Identity("u1", "tenant-a", "pv1", frozenset())
    chunks = [RetrievedChunk("c1", "d1", "tenant-b", "秘密")]
    try:
        enforce_result_scope(identity, chunks, frozenset({"d1"}))
    except PermissionError:
        pass
    else:
        raise AssertionError("越权结果必须阻断")

8. 对抗性审查清单

  • 每个入口都从可信会话读取身份;
  • 关键词、向量、图和原文接口使用同一权限策略;
  • 缓存、会话历史和导出同样隔离;
  • 调试工具有生产禁用和审计;
  • 权限变化使缓存、下载授权和会话失效;
  • 工具参数中的租户 ID 被服务端覆盖;
  • 持续测试水平越权、垂直越权和跨租户访问。

9. 总结

防越权的原则是:身份由服务端确定、检索前限制范围、检索后检测异常、缓存和原文再次鉴权。任何一层都不能把模型当权限裁判。

相关推荐
酷可达拉斯1 小时前
自动化运维-Ansible Role详解
linux·运维·服务器·自动化·ansible
rust&python1 小时前
通达信量化tdxquant快速开始第一个策略
windows·量化·通达信·tdxquant
为思念酝酿的痛1 小时前
Socket编程--TCP
服务器·网络·tcp/ip
牢姐与蒯1 小时前
Linux首战之进度条
linux·运维·服务器
Albart5752 小时前
Windows快速访问幽灵文件夹彻底解决|清除tmp残留_无效固定项_侧边栏残留(Win10_Win11通用)
windows·powershell脚本·快速访问修复·幽灵文件夹清理·tmp垃圾清除·电脑系统优化
caimouse2 小时前
ReactOS 窗口系统架构分析
服务器·c语言
hehelm2 小时前
仿muduo库实现高并发服务器—TCPServer
linux·服务器·网络·c++
王志来137944730083 小时前
聚焦五大核心场景:工控服务器机箱平台的垂直深耕之路
运维·服务器·python
zhonyu鱼3 小时前
RustDesk:开源免费的远程桌面软件,可自建服务器掌控全部数据
运维·服务器·开源