大模型在知识库问答、客服助手和 Agent 场景落地后,往往出现一个尴尬现象:Anthropic、Claude、对齐、安全这些词频繁出现在讨论里,但开发团队真正花时间处理的却是“模型为什么会说出别的客户的数据”“为什么两个人聊着聊着串了上下文”这类问题。围绕模型越权访问事件的讨论,容易让人以为问题出在模型产生幻觉,实际上很多越权来自调用链上缺失的认证、授权和资源归属校验:一个document_id没有按当前用户过滤,一段未做租户隔离的 system prompt,一个被客户端直接传入的user_id,都足以让 Claude 基于不属于当前用户的数据生成回复。
本文不提供对某次具体事件内部结论的猜测,也不搬运无法确认的公司公告细节,而是把这类事件带给工程团队的方法论讲清楚。以 Claude 模型 API 为例,结合 FastAPI 写一个最小可运行的接入层,说明越权访问有哪些形态、模型对齐和应用层安全各自负责什么边界、代码上如何防止水平越权和垂直越权。后面还会给出模型连接类报错的排查路径、发布前可复用的安全检查清单,以及遇到“回答引用了无权访问的内容”时从哪一层开始定位。
1. 先理解大模型场景里的越权访问和传统 Web 越权有什么不同
要防御越权,不能只记住“越权”两个字。大模型应用里的越权,虽然还是“一个用户访问了另一个用户的数据”,但访问的形式多了一层模型生成的过程,很多问题变得不容易通过页面返回或纯数据库查询发现。
1.1 传统 Web 越权的两个基础概念
传统 Web 越权通常分两类。
水平越权指同一角色的用户访问了不属于自己的资源。例如用户 A 登录后修改 URL 里的订单号,结果看到了用户 B 的订单详情。这类问题通常由“服务端只校验了登录态,没有校验资源归属”造成。
垂直越权指低权限用户执行了高权限操作。例如普通用户可以调用管理员删除接口,通常由“接口没有按角色或权限码进行二次校验”造成。
大模型应用并没有摆脱这两种风险,只是增加了新的表现层。API 接收的不再只是“返回某条订单详情”,还可能是“根据某份文档内容回答我的问题”“帮我调用某个工具完成数据变更”。权限判断一旦缺失,返回给用户的不再是一段 JSON,而是一段由大模型润色过的、看似合理的回答。
1.2 模型上下文加入后,越权出现了三种新形态
在与 Claude 等模型 API 集成的服务里,常见的越权形态可以归纳成三类。
| 越权形态 | 典型触发方式 | 表层现象 |
|---|---|---|
| 资源越权 | 请求体传入document_id,后端直接按 ID 加载内容并注入 prompt | 模型引用了无权访问的合同、报告或用户资料 |
| 角色与工具越权 | Agent 或 function calling 根据用户指令调用删除、发送、修改等函数 | 普通用户通过一句话让模型执行了管理员操作 |
| 上下文串用 | 同一个缓存键、Redis 会话或长期连接被多个用户复用 | 用户 A 的对话轮次里出现了用户 B 上一轮的输入或结果 |
资源越权在 RAG 类应用中尤其常见。请求流程往往是这样:前端传入document_id,后端去向量库或文档表取内容,再把内容拼接进 system prompt,最后交给 Claude 生成回答。如果文档表查询没有加“当前用户是否有可读权限”的条件,模型天然会基于“已经传到 prompt 里的内容”作答。它不会像关系型数据库的 SQL 查询那样因为没有权限条件直接报错,反而会把数据组织成一段很流畅的回答,越权在这里是被模型“加工”过的。
1.3 提示词约束靠不住,权限必须是确定性代码
有些开发团队意识到这个问题后,第一反应是在 system prompt 里加一句“你只能回答用户有权限访问的内容”。这句话从技术上没有完全无效,但它拦不住真正的越权。
原因在于,如果后端已经把用户 B 的文档内容拼进 system prompt,模型并不知道这段内容来自哪里,也无法判断这段内容是否属于当前用户。它看到的是“有一份资料,请你基于它回答”,然后就会照做。模型输出是概率性生成,不是安全沙箱;权限判断必须是完全确定的代码逻辑,必须发生在数据进入 prompt 之前。
这也是理解模型对齐与大模型应用安全关系的关键前提:模型层面可以对“说有害内容”进行对齐调整,但代码层面如果把无权限的数据直接喂给模型,再强的模型对齐也无法在应用层替我们兜底。
2. 模型应用的安全边界,不能只盯着模型本身
很多关于模型越权访问的讨论会自然滑向“模型行为不够安全”这个结论。但从工程实现看,一次请求要经过模型服务层和应用层两个完全不同的安全域。两者各有职责,不能互相替代。
2.1 模型侧的对齐工作在解决什么问题
模型对齐是指让模型在训练、微调和部署阶段遵守设计意图,尽量避免输出有害内容、非预期指令或偏离任务目标的内容。以 Claude 系列为代表的对话模型,发布前会经过大量红队测试和安全评估,目标是把“通过概率生成表达出用户不想要的或者有风险的行为”降到可接受范围。
但这类对齐发生在模型层面,应对的是“模型面对任意输入时输出是否安全”。它不会知道你数据库里哪条合同属于哪个租户,也不应该承担这套业务权限判断。模型从 API 收到的输入只有 message、system prompt 和对话历史,这些输入本来就是上层应用拼接好的。只要应用层把内容选错,模型就会认为这些内容可以被正常引用。
2.2 应用侧负责的才是真正的业务安全
对大模型应用开发者来说,应用侧至少要做四类事。
第一,身份认证。确认“当前调用者是谁”,不能相信客户端传来的user_id字段,要通过服务端维护的 token、session 或 OAuth 体系解析用户身份。
第二,授权判断。确认“当前用户是否有权限访问某个资源”或“当前用户是否有权触发某个工具”。这个判断要在确定性的代码、数据库查询条件或权限服务中完成。
第三,上下文隔离。进入 prompt 的内容只能来自已经通过授权校验的资源,并且会话缓存要按用户维度和租户维度隔离。
第四,输出与审计保护。对模型输出做必要脱敏或内容过滤,记录请求 ID、用户、资源 ID、模型调用结果,一旦出现越权或泄露,能够回溯整条链路。
2.3 责任边界要写在协作约定里
Claude API 这类模型服务本身会要求调用方持有 API Key,也就是模型服务只负责确认“调用方是不是有资格使用 API 的账号”。至于这个应用里的普通用户是否看过某份文档,模型服务并不知道。
| 安全职责 | 负责方 | 说明 |
|---|---|---|
| 模型行为对齐、有害内容过滤 | 模型服务商 | 作用于模型输入输出,不感知业务资源归属 |
| API Key 鉴权 | 模型服务商 | 校验调用方身份,不是业务用户身份 |
| 业务用户认证 | 应用层 | 服务端解析 token,不能暴露 API Key 给浏览器或客户端 |
| 资源授权 | 应用层 | 数据库和权限服务拦截无权访问 |
| prompt 内容拼装 | 应用层 | 只向模型提供已授权文档和合法会话上下文 |
| 工具执行校验 | 应用层 | 每次真实数据变更前都要做后端权限校验 |
| 日志审计 | 应用层 | 记录链路标识和访问对象,敏感内容脱敏 |
这条责任边界一旦被违反,往往就会在某个不起眼的地方出现越权。比如有人把 API Key 放到前端直连模型,等于把通道级鉴权当成用户级鉴权;有人把客户端输入直接拼进 system prompt,等于把 prompt 结构化边界让渡给用户。这些问题不解决,模型版本升级得再频繁,业务越权也依然存在。
3. 用 FastAPI 和 Claude API 搭建一个带访问控制的模型调用入口
下面用一个模拟企业文档问答助手的代码示例来说明。这里的代码是工程思路演示,需要在真实项目中结合自己的目录结构、数据库连接和鉴权方式调整。
3.1 环境与依赖准备
先确认开发环境已经具备以下条件。
| 组件 | 用途 | 注意事项 |
|---|---|---|
| Python 3.10+ | 运行示例 | 低版本可能影响 Pydantic 和 FastAPI 行为 |