[控场AI]
· 5 分钟阅读· 2,977 字

AI Agent 越权隐患:用 hallpass 把授权检查交还给系统

AI Agent 越权隐患:用 hallpass 把授权检查交还给系统

AI Agent写操作若沿用Bot凭证,任何对话者都能越权执行操作,应向权威系统查询真实用户权限。

一位开发者在构建运维Agent时发现:即便只留了一条写路径(向GitOps仓库提交PR),Bot使用的token仍可为任何对话者越权开PR,哪怕该用户根本没有仓库推送权限。在系统提示词中添加权限规则毫无作用,因为实际API调用始终携带Bot自身凭证,模型的"判断"无法约束底层权限。正确做法是在工具执行写操作前,直接向GitHub等权威系统查询当前用户是否有权——用户身份来自会话上下文而非模型输出,从结构上堵死了提示词注入篡改身份的可能。他将这套逻辑封装为名为hallpass的独立服务,采用fail-closed策略(结果unknown时拒绝执行),覆盖21个系统,并以装饰器方式接入主流Agent框架。

一个被忽视的写操作漏洞

一位开发者在 Reddit 上分享了他内部运维 Agent 的真实经历,暴露出当前 AI Agent 工程化落地中一个容易被忽略的安全盲区:Agent 执行写操作时,用的是谁的权限?

他的运维 Agent 设计得相当克制。一次工具调用能在大约 15 秒内从各个维度读取某个服务的健康状况,因为读取全部走只读账户,所以他对读操作完全不设限。真正会改变系统状态的路径只有一条——向 GitOps 仓库提交一个 Pull Request。

问题就出在这条唯一的写路径上。Bot 使用的 token 可以为任何与它对话的人打开这个 PR,哪�ть这个人自己根本没有推送该仓库的权限。换句话说,Agent 无意间成了一个绕过权限体系的“通行证发放机”。

reddit 原帖截图

为什么 Prompt 规则救不了你

很多人第一反应是在系统提示词里加一条“只允许有权限的人操作”。但作者一针见血地指出:提示词规则解决不了这个问题。

原因在于凭证的归属。无论模型“相信”调用者是谁,实际发出的 API 调用始终携带的是 Bot 自己的凭证。模型可以被误导、被越狱、被诱导,但底层的 token 权限是真实且恒定的。任何试图用自然语言约束权限的做法,本质上都是把安全边界建立在一个概率模型的“判断”之上——这在安全工程里是不可接受的。

这揭示了 Agent 安全的一条核心原则:身份和授权必须来自可信的会话上下文,绝不能来自模型的输出。

这一问题在安全领域有一个专门的术语:混淆代理人攻击(Confused Deputy Problem)。典型场景是:一个拥有高权限的"代理人"(这里是 Bot)被低权限的调用者利用,执行了原本超出调用者权限范围的操作。Bot 本身并没有被攻破,但它忠实地执行了请求,结果却造成了越权。在传统软件中,这通常通过 CSRF Token、OAuth Scope 等机制来缓解;而在 Agent 架构中,由于意图通过自然语言表达,边界更加模糊,攻击面也随之扩大。Prompt Injection(提示词注入)攻击正是利用了这一点——恶意内容混入上下文后,模型可能被诱导绕过自然语言层面的"权限规则",而底层凭证并不会因此受到任何保护。

解决思路:把授权决策交还给系统本身

作者的思路非常务实:既然 GitHub 早就知道某个人是否有权推送某个仓库,那就直接问 GitHub。

于是工具在执行写操作前会先发起一次询问:这个人可以向这个仓库推送吗?答案有三种——allow(允许)、deny(拒绝)、以及 unknown(无法判断)。关键设计在于对 unknown 的处理:无论是超时、限流还是任何无法评估的情况,只要结果是 unknown,工具就不执行。这是典型的 fail-closed(失败即拒绝)安全策略,避免了因为服务异常而意外放行。

他把这套逻辑抽离成一个独立的小服务,命名为 hallpass,让每一条写路径都能复用它,覆盖 Agent 触及的所有系统——GitHub、Kubernetes、Argo CD、Jira、AWS,总共 21 个。

GitOps 是一种以 Git 仓库作为基础设施和应用配置唯一可信来源的运维模式。所有变更通过 Pull Request 提交、经过审查合并后,再由自动化工具(如 Argo CD、Flux)将仓库状态同步到生产环境。这种模式天然具备审计追踪和权限管控能力——谁能合并 PR、谁能直接推送,都由仓库的分支保护规则和团队权限决定。但这也意味着,如果 Agent 可以以 Bot 身份绕过这套权限体系直接开 PR,GitOps 的安全假设就已经被破坏,审计日志里留下的也只是 Bot 的操作记录,而非真实发起人的身份。fail-closed 策略(失败即拒绝)与之对应的是 fail-open(失败即放行)——后者在服务异常时默认允许操作通过,在安全场景下是危险的默认值。

工程实现:一个装饰器搞定

在 Agent 侧,接入 hallpass 只需要给工具加一个装饰器:

@tool
@guarded(hp, "github-main", "repo.push", "repo:{owner}/{repo}", user=current_user)
def open_config_pr(owner: str, repo: str, patch: str) -> str:
    ...

这里最巧妙的一点是 user=current_user:用户身份来自你的会话,永远不来自模型。因此工具的 schema 里根本没有 user 字段可供模型“说服”或填写——从设计上就堵死了 prompt injection 篡改身份的可能。这种“让越权在结构上不可表达”的思路,比事后校验要可靠得多。

据原帖介绍,hallpass 兼容主流 Agent 框架,包括 Strands、LangChain、LangGraph、Claude Agent SDK,也可以作为 MCP server 运行。它是单个二进制文件,采用 Apache 2.0 开源协议。

MCP(Model Context Protocol) 是 Anthropic 提出的一个开放协议,旨在标准化 AI 模型与外部工具、数据源之间的交互接口。通过将 hallpass 作为 MCP server 运行,它可以被任何兼容 MCP 的客户端(包括 Claude 桌面端、各类 Agent 框架)统一调用,无需为每个框架单独适配。这种架构意味着授权逻辑可以作为独立的基础设施层存在,与具体的 Agent 实现解耦——无论上层用哪种框架构建 Agent,写操作的权限校验都经过同一条路径,便于统一审计和策略管理。

对 Agent 开发者的启示

这个案例的价值不在于工具本身,而在于它点明了一个正在被大量团队忽视的模式:Agent 的读操作可以放开,但每一条写路径都必须绑定真实用户的权限校验。

随着越来越多团队把 Agent 接入生产环境的运维、代码提交、工单和云资源,"Bot 凭证 + 任意对话者" 的组合会成为普遍的攻击面。可行的应对方式有几种:

  • Per-user OAuth:每个用户用自己的授权令牌操作,权限天然隔离;
  • Per-team bots:按团队划分 Bot 权限,降低单点越权范围;
  • 写操作人工审批:所有变更进入人工确认环节;
  • 像 hallpass 这样的授权代理:在工具执行前统一向权威系统查询。

作者也在原帖中向社区抛出了这个问题:你们今天是怎么处理这个问题的?这正是当前 Agent 工程化尚未形成统一最佳实践的一个真实缩影。对于任何正在构建具备写权限 Agent 的团队来说,尽早把授权决策从模型手中拿走、交还给系统,是一条值得认真对待的经验。

#AI Agent 安全#GitOps#权限控制#hallpass#MCP server#LangChain#Prompt Injection#运维 Agent
分享:

相关推荐