跳转到主要内容
指南

如何在不阻止合法 agent 的情况下保护敏感操作

一种实用模型,既能保障账户变更和高影响操作的安全,又能让合法自动化客户端在你的应用中完成有用工作。

Toshendra Sharma

作者 Toshendra Sharma创始人兼 CEO,AgentCredia · Updated November 13, 2026

高级 · 10 分钟阅读

为什么不直接阻止 agent 访问敏感操作?

应用中的某些操作比其他操作更重要。修改密码、添加团队成员、导出数据、删除项目、批准订单: 如果这些操作在没有正确权限的情况下发生,造成的损失是真实的。

与此同时,应用中的合法工作越来越多由自动化完成。用户让助手更新设置。合作方集成按计划创建记录。内部 agent 提交支持工单。把所有自动化客户端都拦在敏感路由之外,会破坏这些工作,并把人推回到手工操作,或者推向更难保障安全的变通办法。

答案不是“阻止 agent”或“信任 agent”。而是一个模型,在这个模型里,操作风险、请求背后的权限以及关于 actor 的证据各自发挥不同作用。本指南将构建这个模型。关于其背后的更广泛思路,请参见为人类和 AI agent 设计应用级策略。

第 1 步: 按风险对路由分级

先对操作分类,而不是对客户端分类。一个简单的三级方案对大多数应用都适用:

级别 示例 默认要求
低 读取公开数据、搜索、查看自己的仪表盘 如有需要,正常身份验证
中 创建或编辑自己的内容、邀请查看者 已认证,且对资源有授权
高 更改凭据、更改所有权、批量导出、删除、批准 授权加升级检查或复核

把级别写入路由配置,这样在代码审查中就能看到:

TS
type Tier = "low" | "medium" | "high";

export const routeTier: Record<string, Tier> = {
  "GET /projects": "low",
  "POST /projects": "medium",
  "POST /projects/:id/members": "medium",
  "POST /account/password": "high",
  "POST /exports": "high",
  "DELETE /projects/:id": "high",
};

级别变化缓慢,也容易推理。本指南中的其他内容都建立在它们之上。

第 2 步: 身份、凭据和授权有什么区别?

有三个问题经常被混为一谈:

  1. 是谁或什么在执行操作? 这是 actor 证据: 一个交互中的人类、一个借助 AI 的人、一个 agent,或者 Unknown,依据可能是 Verified 或 Declared。
  2. 出示了哪种凭据? 会话 cookie、API key、OAuth access token。
  3. 这个凭据是否被允许对这个资源执行这个操作? 这就是授权。

一个已验证的 agent 签名(RFC 9421)很好地回答了第一个问题。它并不能说明第三个问题,这一点在已签名的 AI agent 请求证明什么,以及不能证明什么中有详细说明。一个带有正确 scope 的 OAuth token 回答的是第三个问题。它对第一个问题说明很少。对于高级别操作,你的决策始终需要对第三个问题有一个可靠答案,并利用第一个问题来决定还要追加多少额外保证。

TS
interface Context {
  actor: { actor: "human" | "human_ai" | "agent" | "unknown"; basis: "verified" | "declared" | "inferred" | "unknown" };
  credential: { type: "session" | "oauth" | "api_key"; scopes: string[]; subject: string };
}

function authorized(ctx: Context, action: string, resourceOwner: string): boolean {
  return ctx.credential.subject === resourceOwner && ctx.credential.scopes.includes(action);
}

不会因为 actor 结果看起来有利,就放松授权。actor 上下文只会增加要求,或者在同样安全的路径之间做选择。

第 3 步: 当 agent 代表人类行动时,使用委托授权

当 agent 代表用户行动时,给它一个自己的、权限受限的凭据,不要让它复用用户的会话。OAuth 2.0 是标准工具: 用户为特定客户端批准特定 scope,token 会记录该客户端。RFC 9700,即 OAuth 2.0 Security Best Current Practice,描述了如何安全地部署它。MCP server 采用同样的模式: MCP authorization specification 将受保护的 MCP server 视为 OAuth 2.1 resource server。我们的文章谁在调用你的 MCP server?把这一模式应用到工具调用中。

这正好带来你在敏感操作中需要的能力:

  • token 告诉你哪个客户端在行动,以及它代表谁在行动。
  • scope 限制它能做什么。带有 calendar:write 的调度 agent 不能修改密码。
  • 用户可以撤销授权,而无需更改自己的凭据,且 OAuth 2.0 Token Revocation (RFC 7009) 定义了客户端如何通知 authorization server 该 token 不再有效。
  • 审计日志会显示是 agent 而不是用户执行了该操作。

如果你的应用还不支持委托凭据,应优先为 agent 最常需要的操作提供支持。对于自动化访问来说,这是最有效的单项控制。

第 4 步: 什么时候应该要求升级检查?

升级检查意味着在敏感操作发生时要求额外保证。对于受 OAuth 保护的 API,RFC 9470 定义了 resource server 如何通知客户端,当前 token 的认证强度不够,或者时间不够近。合适的升级检查取决于谁在执行操作:

  • 交互中的人类 (Human, Human + AI): 重新认证(参见 NIST SP 800-63B 中的重新认证要求)、基于 WebAuthn 的 passkey 提示,或者一次性验证码。
  • 带有委托凭据的 agent: 专门用于高级操作的 scope,由用户明确授予,或者发送给用户的带外确认。
  • Unknown: 保持路由现有的强校验,不变。
TS
type Requirement = "none" | "reauth" | "explicit_scope" | "user_confirmation" | "review";

function stepUp(tier: Tier, ctx: Context): Requirement {
  if (tier !== "high") return "none";
  const { actor } = ctx.actor;
  if (actor === "human" || actor === "human_ai") return "reauth";
  if (actor === "agent" && ctx.credential.type === "oauth") return "explicit_scope";
  if (actor === "agent") return "user_confirmation";
  return "reauth";
}

注意,Human 结果不会跳过升级检查。它只是选择适合交互中人的升级检查。推断可能出错,所以它们应该改变检查的形式,而不是取消检查。

第 5 步: 在需要判断的地方路由到人工复核

有些操作罕见、影响很大,而且很难安全自动化: 批量导出客户数据、凭据丢失后的账户恢复,或者删除组织。对于这些操作,复核队列通常比任何自动规则都更合适。

actor 上下文通过向复核者提供证据,让复核更快也更公平: 类别、依据、使用的凭据、类似请求的历史。复核者做决定。分类提供信息。

TS
function shouldReview(route: string, ctx: Context): boolean {
  const reviewRoutes = new Set(["POST /exports", "POST /account/recover"]);
  if (!reviewRoutes.has(route)) return false;
  return !(ctx.credential.type === "oauth" && ctx.credential.scopes.includes("export:bulk"));
}

第 6 步: 按级别选择失败行为

actor 评估可能超时,也可能缺少证据。要提前决定会发生什么。

  • 低级别: 失败开放。请求正常继续。
  • 中级别: 失败开放,同时保留正常授权。
  • 高级别: 失败封闭到现有的强校验。缺失的 actor 证据绝不会削弱保护,也绝不会阻止原本会通过正常检查的请求。

关键属性是对称性。actor 评估中的失败,应让操作保持与添加 actor 上下文之前完全相同的安全性,也完全相同的可用性。

第 7 步: 不要基于推断对人做出有后果的决定

推断出的分类,是关于一次交互的证据,不是关于某个人的事实。不要因为某个会话看起来是自动化或 AI 辅助的,就拒绝某人的求职申请、信贷决定、考试结果、住房,或基本服务的访问权限。在这些流程中,使用 actor 上下文来选择流程,例如人工复核或替代验证路径,并确保当事人可以完成它。

这既是公平原则,也是实用原则。推断会产生误报,而这些流程中的误报代价,会落到一个没有做错任何事的人身上。

第 8 步: 先以观察模式推出

在这些措施影响真实请求之前,先以观察和预览模式运行:

  1. 记录级别、actor 类别、依据、凭据类型,以及你的策略本应要求的升级检查。
  2. 按类别抽样复核高级别请求。合法 agent 是在使用委托凭据,还是在复用用户会话?
  3. 先修复你发现的缺口,通常是提供带范围的凭据,然后再执行。
  4. 先在单一路由的小范围上执行,衡量完成率,然后再扩大。

第 9 步: 保留能回答“是谁以及代表谁”的审计轨迹

对于每一次高级别操作,都要记录足够的信息,以便日后重建发生了什么:

  • 请求发生时的 actor 类别和依据,
  • 凭据类型、它被签发给的客户端,以及它所代表的 subject(OAuth 2.0 Token Exchange,RFC 8693,定义了一个 act claim,用来准确记录这类“代表谁行动”的关系),
  • 授权该操作的 scope,
  • 应用的升级检查或复核要求,以及其结果。
TS
audit.write({
  action: "project.export",
  actor: ctx.actor,
  client: ctx.credential.type === "oauth" ? clientIdFromToken(req) : undefined,
  subject: ctx.credential.subject,
  scopes: ctx.credential.scopes,
  requirement,
  outcome: "approved",
  at: new Date().toISOString(),
});

有了这条轨迹,像“是某个人还是某个助手删除了这个项目,谁批准的”这样的问题就能得到清晰答案。这能让事故响应更快,也让用户相信,应用中的自动化帮助是有责任可追踪的。

一个完整示例

一个项目管理应用看到助手为用户更新任务截止日期,并且偶尔尝试导出整个项目。

  • 更新任务属于中级别。助手使用带有 tasks:write 的 OAuth token。已授权,无需升级检查。允许。
  • 导出项目属于高级别。助手的 token 缺少 export:bulk。应用向用户发送确认请求。用户批准后,应用为该客户端授予该 scope。
  • 一个 Unknown 客户端尝试用复用的会话 cookie 执行同样的导出。现有的重新认证提示会按原样生效,和以前一样。

没有人因为是 agent 而被阻止。每个高影响操作都带有明确授权。

Proof of Actor 在这里的作用

Proof of Actor 提供这个模型中的 actor 侧: 谁或什么在行动,以及其依据是什么。你的应用保留身份验证、授权和最终决策。Detect actor,Understand 每一类如何使用你的敏感路由,并为每个请求 Route 到合适的保证级别。

如果你想和我们的团队一起为你的应用设计这套方案,请申请早期访问。关于更详细的 SaaS 场景,请参见 SaaS Apps & Websites。

来源

与我们一起构建。

加入 Proof of Actor founding developer program。

想先聊聊? 联系创始人