← TELOA BLOG

安全研究 · 2026年10月11日 · Max Luo

授权如何落到执行:V2、V3、V4 的三道边界

由 Teloa AI Security 维护 · 智能体安全基线系列

作者:Max Luo · 2026年10月11日 · Teloa AI Security

员工有权读工单,Agent 能调用发送工具,连接器持有可访问全库的密钥。这三个事实相加,不能推出“Agent 可以把客户库发到任意地址”。授权必须回答:哪个可信主体,在当前权限与任务范围内,能否通过这条执行路径,对这些资源产生这项具体效果?

本文把基线 V2、V3、V4组合成一条参考执行流程,讨论权限计算、审批参数漂移、撤销竞争、网关绕过和重试。示例面向包含 L2 控制的企业部署;哪些属于 L1、L2 或 L3,以原条款为准。代码是教学内核,不是可直接部署的完整授权系统,也不代表 Teloa 的实现或符合性结论。

1. 权限交集要落到操作和资源,不能只比较角色名

“用户属于项目组”和“Agent 被授予工单读取”仍不足以允许某一次调用。项目组可能只能读某个租户的工单,Agent 可能只能读一个项目,任务可能只需要某个月的数据,委托又可能限制最大导出数量。

P_effective(t) = P_user(t) ∩ P_agent ∩ P_task ∩ P_delegation
                再满足环境、参数、风险及配额约束

这是参考模型。V2.2.1 的用户与 Agent 权限交集是 L1;任务范围与逐次重新计算是 L2 控制。每个权限集合表达允许的操作与资源,而不是把 admin、reader 等字符串作集合交集。数据标签、金额、收件人、时间窗口等约束还要单独求值。

例如用户可读 project-a、Agent 只可读 project-a 的工单,任务仅覆盖 10 月。最终查询必须受这三个范围共同限制。调用子 Agent 后,其较广的连接器权限也不能扩大交集。共享连接器的权限是凭证能力,不能用作用户授权依据。

超过用户权限的请求应拒绝。超过任务或 Agent 范围的请求即使转为审批,也要先走有效的范围变更或临时授权流程,重新计算权限;用户点一下“确认”不会产生其原本没有的资源权限。

2. 用一个可运行内核说明拒绝优先与外发审批

下面的 Python 3.9+ 示例使用精确的“操作、资源”对演示权限交集。operation 由可信工具目录和实际参数推导,范围集合与状态由控制面取得;不能把模型提供的风险类别或 user_scope 直接传入。生产实现还需要租户隔离、条件策略、数量限制、审批状态和审计。

from dataclasses import dataclass

Permission = tuple[str, str]

@dataclass(frozen=True)
class Context:
    user_active: bool
    agent_active: bool
    user_scope: frozenset[Permission]
    agent_scope: frozenset[Permission]
    task_scope: frozenset[Permission]
    delegation_scope: frozenset[Permission]
    untrusted_input: bool = False


def decide(operation: str, resource: str, ctx: Context) -> str:
    if not ctx.user_active or not ctx.agent_active:
        return "deny"
    known_operations = {"read", "write", "delete", "send"}
    if operation not in known_operations:
        return "deny"
    effective = (ctx.user_scope & ctx.agent_scope &
                 ctx.task_scope & ctx.delegation_scope)
    if (operation, resource) not in effective:
        return "deny"
    if operation == "send":
        return "require_approval"
    if operation in {"write", "delete"} and ctx.untrusted_input:
        return "require_approval"
    if operation == "delete":
        return "require_approval"
    return "allow"


from dataclasses import replace
scope = frozenset({("read", "project-a"),
                   ("send", "summary-17")})
ctx = Context(True, True, scope, scope, scope, scope)
assert decide("read", "project-a", ctx) == "allow"
assert decide("read", "project-b", ctx) == "deny"
assert decide("send", "summary-17", ctx) == "require_approval"
assert decide("read", "project-a", replace(ctx, user_active=False)) == "deny"

示例将全部发送(包括企业内和外部)转为审批,是一个较保守的部署选择;V6.2.1、V7.5.4 至少要求接触不可信内容或非公开数据后默认阻止外发,审批后才可执行。require_approval 只是待审批状态,不是执行许可。提前批准的计划需另建受控状态,并校验每次调用仍在已批工具、范围和数量上限内。

策略服务出错、超时或返回无法识别的决定,应拒绝执行,对应 V3.2.4。工具清单裁剪可以减少模型提出无权调用的机会,但执行前仍必须校验。策略和钩子应在 Agent 无法修改的控制组件中运行。

3. 审批绑定实际效果,阻止批准后换参数

攻击者不一定要伪造一次审批。它可以等待用户批准“发送摘要”,然后将收件人替换成外部地址,把摘要换成原始客户库,或让同名工具更新后的参数产生更大的影响范围。

平台应先根据已解析且补齐默认值的真实调用参数,生成不可变的效果描述,再生成审批展示。发送类操作可以包含:

{
  "approval_id": "approval-73",
  "request_id": "req-17-02",
  "principal": ["tenant-a", "user-42", "agent-support", "run-17"],
  "tool": "mail.send",
  "tool_version": "sha256:reviewed-tool-schema",
  "recipients": ["[email protected]", "[email protected]"],
  "recipient_resolution_revision": "directory-73",
  "content_ref": "blob-summary-17",
  "content_sha256": "sha256:immutable-reviewed-content",
  "attachments": [],
  "policy_version": "policy-23",
  "nonce": "single-use-nonce-73",
  "status": "approved"
}

示例中的摘要字符串是占位标识,实际实现应使用相应算法计算出的真实值。正文哈希绑定内容,但不能替代审批人认证、审批权限和完整记录保护。平台还要记录批准者、有效期、已批准参数与认证强度;展示内容由平台根据实际参数生成。审批的是一个不可变内容对象,执行时要从该对象读取,不能接受“相同 ID、内容却已更新”的文件。

如果使用参数摘要,应在类型化解析、规范化与默认值展开后计算。处理重复 JSON 键、Unicode、路径、金额单位及收件人解析等歧义;不能直接哈希一段未经解释的字符串。RFC 8785提供 JSON 规范化方案,但它不定义工具参数的业务语义。收件人排序、金额精度与资源版本仍需由工具协议明确。

收件人还存在语义漂移:邮件组名称不变,成员却可能增加。效果描述应绑定解析后的实际收件人与成员版本,派发前复查,不能只比较 [email protected] 这个字符串。无法冻结下游邮件组展开结果时,不向该组发送敏感正文,可以改发按访问者身份鉴权的受控链接。接收者权限变化也需要重新检查。

执行时重新核验主体、工具版本、参数与内容摘要、有效期、审批状态及当前权限。任何变化都失效或重新决策。资源变更类工具还可以绑定资源版本,并在下游支持时使用条件更新,防止“批准对象”在等待期间已变成另一个状态。V3.4.6 的事先批准计划需要校验包含关系和累计配额,不能退化成“这个 Agent 以后都已批准”。

4. 处理审批与执行之间的竞争窗口

审批完成不是最终状态。用户可能在等待期间被禁用,策略可能收紧,Agent 可能被停用,审批可能已过期。授权决策缓存如果没有时长上限,会把一次合法授权变成长期通行证。

PROPOSED → NORMALIZED → POLICY_CHECKED
                    → AWAITING_APPROVAL → APPROVED
                    → REVALIDATED → DISPATCHING → SUCCEEDED
                                             → FAILED / UNKNOWN

REVALIDATED 在实际派发前重新检查当前权限与身份、已批参数和工具版本。策略版本变化后应重新求值,不能继续信任旧 allow。审批的单次消费与派发记录应原子关联,避免两个 worker 同时使用同一批准。

但网关的本地事务无法与任意外部 SaaS 形成统一事务。若下游已发送消息而响应丢失,状态是 UNKNOWN,不应为了得到一个成功响应而盲目再次发送。优先使用下游支持的幂等键;重试同一次效果时复用同一键,并查询先前结果。没有幂等与结果查询能力时,转为人工核对。去重记录要绑定主体、工具和效果描述,不能让另一组参数借用旧请求 ID。

权限复查与外部效果之间仍可能存在短暂竞争。缩小派发窗口、阻止停止状态续期、对下游使用受限短期令牌并接入撤销机制,可以降低窗口风险;系统需要明确已在途操作如何处理。不能仅靠“每次都查一次权限”声称已经消除了全部 TOCTOU 风险。

5. 网关必须约束真正的请求路径与凭证目标

V4 控制实际效果:执行环境只获得占位凭证,网关依据已认证主体和授权决定取用真实凭证,并只向绑定目标注入。对于平台托管环境,网络默认拒绝出站,下游也拒绝来自执行环境的绕网关直连。只设置 HTTP 代理环境变量不能强制任意程序走代理。

一个可实施的网关检查顺序是:

  1. 认证工作负载并恢复可信主体,检查实例和任务仍活动。
  2. 从受控工具定义解析操作、参数及目标连接器,拒绝未经审核的工具版本。
  3. 验证本次决定与真实请求一致,复查审批、权限、配额和当前策略。
  4. 将目标解析为登记的协议、主机、端口、方法和路径范围,限制 DNS 与实际连接地址,防止目标替换与重绑定。
  5. 向凭证库请求该用户或非人类主体的连接,检查目标绑定,再注入凭证。
  6. 执行请求并关联下游效果;净化返回内容,避免错误消息或调试响应把凭证带回模型。

允许域名不能用 startswith("https://trusted.example") 实现,这会误放行其他主机名。路径也要按服务协议处理编码、规范化和边界。重定向默认不自动跟随;如果业务需要重定向,新目标必须重新经过检查,旧凭证不能跨目标转发。对内网访问按已登记服务与地址范围放行,不能用一个笼统的内网允许规则恢复横向访问能力。

凭证库只保存到用户或 Agent、连接器和目标的受控绑定。调试日志中不出现 Authorization 头;正常工具返回也不能反射真实密钥。审计记录凭证取用事件和连接标识,而非凭证值。

无法由网关注入的客户端签名凭证,例如某些云 API 请求,按 V5.2.2 使用限定用户、任务、权限和资源的短期凭证。这个例外需要单独评估执行隔离与网络边界,不意味着可以把长期云密钥放入模型上下文。

6. MCP 与遗留系统各有额外边界

MCP Server 的合法令牌不能随意传给下游 API。MCP 的授权规范要求校验面向本 Server 的令牌,并禁止令牌透传。对应基线 V4.5.5,MCP 与下游 API 是不同的授权跳:分别使用正确的受众和授权形式;本地 MCP 进程也不能绕过出网限制。

发送方绑定可以降低被盗令牌在其他客户端重放的风险,RFC 9700讨论了 mTLS、DPoP 与受众限制。它们仍不能让一个已经失陷、能使用合法密钥的实例自动变安全,资源级策略依然需要执行。

遗留系统若只支持共享静态凭证,V4.4 要求网关补做用户级、接口级和操作级授权。例如下游只有一个能访问全部项目的数据库账号,网关不能因为共享账号可查询就允许任意 SQL;它需要把可信租户和资源范围落实到受控查询,限制操作,并防止其他路径绕过。同一个共享账号在下游日志里不能区分用户时,应通过网关审计和适用的签名身份信息建立关联,记录该补偿方案的局限与复核责任。

7. 十二个失败用例验证三道边界是否闭合

用例 预期处理 必须检查的实际效果
用户有权、Agent 无权 权限交集拒绝 下游调用为零
Agent 有权、用户无权 拒绝,审批不自动补权 无越权读取与写入
任务只读,模型提出全量导出 拒绝或进入有效的范围变更流程 旧任务范围未被静默扩大
子 Agent 持有更广的连接器 仍受根用户与委托范围约束 没有新的资源可见性
审批后变更收件人、邮件组成员、正文或附件 效果描述或可见范围不一致,拒绝执行 原批准没有被消费为另一个效果
审批过期或工具定义更新 重新审批或拒绝 新工具不继承旧批准
策略服务超时、返回畸形响应 默认拒绝 无直接调用回退
用户禁用后复用旧 allow 按撤销与缓存规则阻断 网关停止取用凭证
两个 worker 并发消费审批 只派发一次逻辑效果 去重记录与下游效果一致
下游成功但响应丢失 幂等查询或人工核对 重试未重复发送
沙箱直连、换域、跳转目标 访问路径或目标检查拒绝 密钥未进入新目标
将 MCP 令牌用于下游 API 受众校验或独立授权跳阻止 原令牌没有透传

每条记录至少关联用户、Agent、实例、任务、操作参数、策略决定、审批以及下游结果,对应 V10.1。敏感内容与身份信息应按 V10.2 控制调阅。除了保存“拒绝日志”,还要核对外部系统没有发生效果;对 UNKNOWN 单独追踪,不能把未知结果当成安全失败。

8. 评审时逐项问实现能否证明什么

对 V2,要求团队展示权限交集与委托范围的来源,以及用户降权后的变化。对 V3,展示平台生成的真实参数审批、不可绕过的检查和默认拒绝。对 V4,展示受控的网络路径、凭证目标绑定,以及执行环境无法取得真实长期凭证的证据。

这样的分工使缺口可定位:如果参数校验正确但直连可绕过,修复在实际访问边界;如果网关路径受控但用管理员连接器代替用户权限,修复在授权;如果审批只绑定模型摘要,修复在效果描述与参数一致性。把责任放在能强制执行的组件上,才能将一套设计思考转成可复验的安全基线。

回看威胁建模与评估篇与身份篇,使用自评工具整理证据。完整标准、架构与工具源码由 Teloa AI Security维护,源于 Teloa 的开发实践。