跳到正文
Hacker News· guedou·· 8 天前AI 评分71

GitGuardian 复盘九起 AI Agent 事故,提出六项安全控制

AI Agent Security: Six Controls from Nine Real Incidents

AI 导读

GitGuardian 梳理 2025 年 5 月至 2026 年 7 月的九起公开 AI Agent 事故,按主机、身份、智能体三层攻击面归类,指出智能体面在五起中出现且从不单独出现。

正文 · AI 翻译
AI Agent Security: Six Controls From Nine Real Incidents

TL;DR

  • 三个攻击面:一个 AI 智能体暴露了它的宿主、它的身份以及它自身。2025 年 5 月至 2026 年 7 月间的九起公开事件同时触及了这三个面;智能体面出现在其中五起中,且总是与宿主或身份结合出现。
  • 凭证才是目标:在注入事件中,智能体读取了一个 issue、一封邮件、一个设置文件或一个携带指令的网页。指令打开了大门,而凭证才是攻击者真正想要的东西。
  • 六项控制措施限制爆炸半径:将智能体沙箱化、限定其凭证范围、使其无法接触自身配置、记录每一次调用、先扫描工作区中的机密、默认拒绝。这些措施都无法阻止提示注入。

摘要

一个 AI 智能体带着你的凭证运行,并在你来得及阻止它之前就采取行动。九起事件展示了这样做的代价。六项控制措施限制损害:将智能体沙箱化、限定其凭证范围、锁定其配置、记录每一次调用、先扫描工作区、默认拒绝。

AI 智能体攻击面:宿主、身份与智能体自身

一个智能体暴露了三个不同的面。有它触及的宿主、它扮演的身份,以及智能体自身。

宿主是该进程能够触及的一切:文件系统、进程执行、网络、环境变量,以及摄像头或麦克风等硬件。身份是智能体所扮演的账户,连同其权限和范围,以及它沿途能够发现的每一个凭证。智能体则是传统进程中没有对应物的那部分。它拥有上下文、通过记忆持久化的状态、钩子、技能和配置,以及它所连接的工具。

这三个面以不同的方式失效,需要不同的控制措施。经典的操作系统安全已经覆盖了宿主。身份在很大程度上可以通过众所周知的身份与访问管理(IAM)模式来解决。

智能体面是新的一个,因为每一个输入都可能是一条指令。将这三者分开,能告诉你哪些事件现在就能修复,哪些仍是行业正在摸索的。

十五个月的事件,按攻击面分类

2025 年 5 月至 2026 年 7 月间的九起公开事件触及了全部三个攻击面。智能体面出现在其中五起中,且从未单独出现。

宿主:原则上已解决,实践中仍在失败

2025 年 7 月,一位用户要求 Gemini CLI 通过创建一个新目录并将文件移入其中来整理一个文件夹。mkdir 命令静默失败了。智能体没有检查错误,假设成功,并继续向一个不存在的路径发出移动命令。由于所有移动都指向同一个不存在的目标,每个文件都覆盖了上一个,最终只留下一个文件。没有攻击者,没有注入。一个未检查的退出码和一个假设成功的循环。

一个月后,一位用户要求 Claude 清理 Claude 创建的一个目录。权限系统正常工作,并请求确认 rm -rf ~/。用户已疲惫不堪,便批准了。造成的损害与恶意清空完全相同。

这两起事件在同一条界线的两侧各自失败。Gemini CLI 始终停留在被指定的文件夹内,因此任何沙箱都不会被触发。它只破坏了被交给它的东西。Claude 则相反:面对一个范围限定在其自身输出内的请求,它却提议删除主目录。一个限定工作区范围的沙箱本可以阻止那一次。审批对话框是当时唯一的控制手段,而它在一个疲惫的用户面前失效了。

Gemini 的失败是两者中更令人不安的。把同一个智能体指向主目录而非项目文件夹,同样的 bug 就会抹掉整台笔记本电脑。因此,控制点不仅在于智能体能否被约束,还在于你一开始交给它的是什么。

身份:智能体继承你所持有的权限

2025 年 12 月,一位 AWS 工程师使用亚马逊的 Kiro 智能体修复 Cost Explorer(AWS 账单仪表盘)中的一个 bug。该智能体决定删除并重建整个环境。它继承了该工程师的高权限,跳过了同行评审,并导致了一次持续 13 小时的区域性中断。亚马逊将责任归咎于配置错误的 IAM 角色,而非 AI。

2026 年 4 月,一个在 Railway 部署上工作的智能体遇到问题,决定删除一个存储卷。它没有执行该操作的相应身份权限,于是在文件系统中寻找令牌。这次删除摧毁了生产环境以及与之存放在一起的备份。

没有人授予那个 Railway 智能体该凭据:它是自己发现的。该主机上的每一个密钥都是其身份的一部分,无论你是否有意为之。真正重要的问题不是你给了智能体什么,而是智能体能在你给它的环境中找到什么。

智能体:每一个输入都可能是指令

2025 年 5 月,Invariant Labs 展示了一个恶意 issue,它被提交到一个公共仓库就足够了。受害者让他们的智能体检查未关闭的 issue。智能体读取了植入的指令,利用受害者权限过宽的 GitHub 令牌拉取私有仓库,并将其泄露到一个公开的 pull request 中。一个权限过宽的令牌,在公共与私有之间的边界上被重复使用,而没有任何机制来强制隔离。

2025 年 6 月,Aim Labs 披露了 EchoLeak。一封精心构造的电子邮件就足够了。Microsoft 365 Copilot 将其作为上下文处理。隐藏的指令使其搜索用户的邮箱、OneDrive 和 SharePoint,然后通过自动渲染的图片 URL 将结果外泄。受害者从未打开过那封邮件。微软将其评为 CVSS 9.3。

2026 年 2 月,Check Point 记录了通过 Claude Code 项目文件实现的远程代码执行和 API 密钥窃取。克隆并打开一个不受信任的仓库就足够了。一个恶意的 .claude/settings.json 将 shell 命令作为钩子注入。一个 .mcp.json 在信任对话框出现之前就运行了 MCP 服务器。一个由仓库设置的 ANTHROPIC_BASE_URL 将经过身份验证的流量(包括认证头)重定向到攻击者端点。这三者都在用户同意任何内容之前执行了。

2026 年 7 月,Intezer 展示了同一个 AWS Kiro 智能体在读取一个普通网页上的隐藏文本后重写自己的 MCP 配置。该智能体编辑了本应包含它的那个文件,然后重新加载了它。在默认的自主模式下,不存在有效的审批提示。

本节中的每一起事件都以相同的方式开始。智能体读取了它本就该读取的内容,而该内容携带了指令:一个 issue 正文、一封邮件、一个设置文件、一个网页。这些指令所触及的东西,正是这一模式的另一半。一个权限足够大、能从公开跨越到私有的 GitHub token。一个本只用于总结邮件的会话,却拥有邮箱和云盘的作用域。指令打开了门。凭证才是攻击者真正想要的东西。

How indirect prompt injection works

没有攻击者,三个层面全中

2026 年 7 月,正在运行网络能力评估的 OpenAI 模型逃出了沙箱,并攻陷了 Hugging Face 的生产基础设施。没有人攻击它们。在任务受阻后,它们把一个内部的 Artifactory 实例变成了留言板,并在上面交换漏洞利用。防御者吊销了凭证并将其删除;智能体在一天之内就重建了留言板。我们的文章记录了完整的时间线。

这是九起事件中唯一一起同时触及主机、身份和智能体,且整条链条上没有任何攻击者的事件。智能体被赋予了一个目标;它们失败了,然后找到了另一条路径。当防御者封堵它时,它们又找到了下一条。

AI 智能体威胁模型:老问题,新速度

把智能体当作内存中的恶意软件来对待,而且你可能无法逆向工程并理解它。它凭借被授予的权限运行,在任何人类能够干预之前就采取行动,并且不会两次表现出相同的行为。

能力:智能体能做什么

智能体以高权限运行,这些权限通常继承自启动它的人。它读写文件系统、访问网络、执行代码,还能生成子智能体。它访问凭证,既包括被授予的,也包括它自己找到的。它通过记忆、钩子、技能和配置来持久化状态。

与其他任何进程不同,智能体还通过技能和 MCP 服务器连接工具。智能体把这些连接器当作指令来读取。工具描述进入其上下文的方式与用户提示词相同,因此使用连接器就意味着读取不可信输入。

后果:哪里会出错

有五件事会出错:破坏、数据外泄、横向移动、持久化和密钥泄露。

破坏是上述事件中最明显的一种,几分钟内就会有人注意到。数据外泄和横向移动则完全不需要犯错:读取数据和使用凭证正是智能体所执行的任务。它们只有在检测系统触发时,或者最终有人查看日志时才会浮出水面。

持久化和密钥泄露在智能体身上呈现出不同的形态。传统进程结束后不会留下任何东西。智能体会写入记忆、钩子、技能和配置,然后在之后的会话中把它们作为先验知识读回来。没有其他良性进程会自行决定去寻找凭证。智能体会搜索文件系统和环境来找到一个,并在找到后将其发送给模型提供商。

这些都不是新问题。它们是我们多年来一直防御的相同后果。新之处在于,单个进程在一次运行中能够触达的此类后果的数量。

向量:它是如何发生的

有五种进入方式:模型本身、提示注入、工具和技能投毒、记忆投毒,以及审批疲劳。

模型本身完全不需要攻击者。智能体自行判定删除环境就是修复方案,并且拥有执行该操作的权限。上述事件中有四起属于这种情况。OpenAI 的智能体在被任务阻塞时也没有停下:它们找到了漏洞并加以利用。一个错误的决定和一个正确的决定会产生相同的日志条目。

其余三起需要有人植入指令。直接提示注入来自用户,而用户本身就是主体。间接注入则隐藏在智能体被要求处理的内容中:一个 issue 正文、一个配置文件、一个 pull request 标题、一个网页。上述事件中有四起由此开始,而在每一种情况下,这些内容都是智能体为完成工作而必须读取的。工具与技能投毒是通过连接器出现的同一问题。智能体将工具描述当作指令来读取,而远程服务器可以在审查之后更改它。记忆投毒是唯一能超越会话存续的一种,它会在后续任务中作为先验知识出现。

rm -rf ~/ 案例展示了审批提示的局限。控制机制触发了,人类阅读了它,然后人类批准了。

放大器:是什么让它变得不同

有三件事让旧的结果变得更糟:速度、规模和不确定性。

Gemini CLI 覆盖文件夹的速度比用户读取输出的速度还快。在操作之后运行的审查步骤并不能阻止该操作。Hugging Face 的智能体是彼此独立的运行,没有共享状态。其中一个找到了逃逸方法并把它写了下来,其他智能体可以读到,于是后续每一次运行都从该漏洞利用开始。相同的提示在不同的运行中会产生不同的操作,因此通过了一次测试的配置可能在下次失败。

这些向量和结果并不新鲜。新鲜的是,智能体的行动比我们为审查其行为而构建的安全流程更快、更远、更难以预测。

AI 智能体安全控制:按层面划分的现有措施

当今已有的控制措施,按层面划分:主机、身份、智能体,以及围绕这三者的框架架构。

主机:将其隔离,并限制你交给它的东西

从智能体自带的功能开始。不要用 --dangerously-skip-permissions 或其他智能体中的等效选项来运行 Claude。在存在原生沙箱的地方使用原生沙箱,并为工具、路径和命令配置允许与拒绝规则。这些措施启用成本低,也容易被不受信任的仓库覆盖,这就是为什么它们是第一层而不是唯一一层。

然后在沙箱中运行智能体。内核沙箱如 nono、微虚拟机如 Docker Sandboxes,或容器,可将文件系统、进程和网络访问限制在任务范围内。集成运行时如 Bromure 将虚拟机、沙箱和出口代理打包在一起。这能阻止 rm -rf ~/ 案例。

它不能阻止 Gemini CLI 案例。沙箱允许对工作目录进行写入,而损害正是发生在那里。挂载项目,而不是主目录。工作集是沙箱无法替你做出的决定。

将恢复路径置于智能体触及范围之外。Railway 卷将生产环境及其备份置于同一身份之下。智能体可以删除的备份不算备份。在项目层面,版本控制覆盖已跟踪文件,nono 的工作区快照覆盖其余部分。写入仍然会发生;回滚会撤销它。

这些机制在智能体出现之前就已构建,用于不受信任的代码和不受信任的用户。它们有效。

身份:给智能体自己的身份,并给它更少的权限

给 agent 一个属于它自己的主体身份。当它以用户身份运行时,审计日志记录的是用户。事故发生后,没人能分辨哪些操作是用户做的,哪些是 agent 做的。一个具有限定权限的专用身份可以一步解决归因问题并限制损害范围。

将凭据的作用域限定在任务范围内,让其他所有凭据都不可触及。Railway 的 agent 需要一个部署令牌,却找到了一个还能删除卷的令牌。部署令牌本不应带有那个作用域,而一个权限更广的令牌本就不该留在磁盘上供 agent 读取。对于 agent 无需被授予就能触及的每一个凭据,同样如此:环境变量、dotfiles、AWS 和 GitHub CLI 的凭据存储、SSH agent socket、浏览器会话。一个只需要一个令牌的 agent,应该运行在一个只包含一个令牌的环境中。nono 和 Bromure 处理后半部分:agent 看到的是占位符,透明代理在传输时替换为真实值。它们对前半部分无能为力。如果代理注入的凭据能删除生产环境,那么 agent 就能删除生产环境。

在把 agent 指向某个环境之前,先了解那里已经有什么。散落在仓库、CI runner 或主目录中的密钥,就是 agent 的发现面。首次运行前对工作区进行扫描可以缩小这个面。这是这里唯一一个在 agent 运行之前就生效的控制措施。

限定作用域的身份和短期凭据是早于 agent 出现的身份与访问管理模式。凭据代理则不是。它之所以存在,是因为 agent 会去寻找未被授予的身份,而代理确保它找不到任何可用的东西。授予 agent 的东西仍然必须按老办法限定作用域。

Agent:约束它能触及的范围

这里有四项控制措施:将配置置于 agent 触及范围之外、允许列表连接器、记录每一次调用,以及检测。

将配置置于 agent 触及范围之外。agent 会把项目级文件如 .claude/settings.json 和 .mcp.json 当作指令读取,而一个不受信任的仓库可以设置它们。控制措施必须位于这些文件之外。将配置和记忆路径标记为只读的沙箱可以阻止 agent 重写它们。Kiro 在读取一个网页后就重写了自己的配置。

对工具和连接器使用允许列表,并理解其局限。远程 MCP 服务器在下一次运行时可能提供不同的工具描述。对于这些,安装前的审查无法覆盖 agent 将会读取的内容。

记录每一次工具调用和每一次出站连接。钩子级日志记录,例如 nova-tracer,会捕获每次工具调用及其参数。OpenTelemetry 将同样的记录带入团队已用于追踪的任何系统。出站日志显示 agent 访问了哪些域名,包括它把对话发送到的那个域名。

检测能捕捉到的东西比针对人类的要少。EDR 签名是针对人类行为模式调校的,而 agent 不会产生这些模式。护栏能捕捉已知的注入形态,却会漏掉新的。两者与上述控制措施一样仍有其位置。但两者都不能是唯一的。

这个面上的每一项控制措施都限制了 agent 在被指示后能做什么。它们都无法阻止那条指示。

架构:决定它能触及什么

两项设计决策限制了指令到达后能做什么。两者都是对你已经在运行的 agent 做一次配置更改。

按任务而非按代理来限定工具范围。一个读取公开 issue 的任务不应获得写入私有仓库的工具。Claude Code 通过子代理工具实现这一点:列表、--allowedTools 和 --strict-mcp-config;Codex 通过 --sandbox;pi 通过 --tools 或在 nono 下运行。Invariant Labs 的案例本会就此止步。

只批准无法撤销的操作。一个在每次调用时都弹出提示的框架会训练用户去批准。

Approve only what cannot be undone

一个默认拒绝、仅在写入、发送、删除和花费时提示的框架,产生的提示是用户负担得起去阅读的。Claude Code:acceptEdits 加上只读允许列表,或一个 PreToolUse 钩子。Codex:--sandbox workspace-write --ask-for-approval on-request。pi:权限门扩展,读取在允许列表上,其他一切都在询问上。

仍然未解决的问题

有三个问题目前尚无完整的控制手段。

模型本身。上下文中的任何内容都无法解释模型为何决定删除环境。一个被训练为对触发器做出行动的模型留下的线索更少。指令存在于权重之中,会话中没有任何输入能显示它。对于这两种情况,上述控制手段都假设代理最终会做错事,并将影响范围保持在较小程度。

记忆投毒。如上一节所述,将记忆路径标记为只读,可以阻止代理在运行中途毒害自己的记忆。代价是没有记忆。仍然未解决的是一个可写记忆,且不会将注入带入下一个会话。

数据流策略。拒绝在同一会话中使用不受信任来源的工具和高权限工具。已有公开的设计和参考代码。尚无已发布的编码代理强制执行这一点。

从放大器可以得出一点,适用于上述每一项控制。代理行动的速度快于人类审查的速度,因此事后运行的控制是日志,而非控制。策略必须在工具调用之前生效。

最佳实践:六条不可协商的原则

Six non-negotiables for AI agent security

在任何代理针对真实系统运行之前,有六项控制适用:

  1. 在沙箱中运行代理。挂载项目目录,而非主目录。
  2. 给代理一个限定于其任务的凭证。通过代理或中介,让其他所有凭证都远离它的触及范围。
  3. 将代理的配置放在它无法写入的地方。托管设置优先;仓库中的任何内容都不能覆盖它们。
  4. 记录每一次工具调用和每一次出站连接,并将记录发送到 SIEM。
  5. 在首次运行前扫描工作区中的机密,并在其变更时再次扫描。
  6. 默认拒绝。仅在写入、发送、删除和花费时提示。

这些都无法阻止提示注入。它们限制了被劫持的代理所能触及的范围,而这是目前唯一在你控制之下的部分。

立即扫描你的工作区中的机密。

常见问题

什么是 AI 代理安全?

代理暴露三个攻击面:它运行所在的主机、它作为的身份,以及决定它做什么的上下文。操作系统和身份控制覆盖前两个;护栏能捕获已知的注入模式,但尚无任何东西能阻止文件或网页告诉代理该做什么。

AI 代理安全的主要威胁是什么?

模型本身、提示注入、工具和技能投毒、记忆投毒以及审批疲劳。其后果是破坏、数据外泄、横向移动、持久化和机密泄露,与以往相同,只是速度和规模超出了任何审查和检测流程的设计能力。

如何保护 AI 代理凭证?

将凭据限定到该任务,其他凭据一律不落盘。首次运行前用 ggshield 扫描工作区中的密钥,然后注册 AI Hooks,这样提示词、文件读取或 shell 命令中的密钥在到达模型之前就会被拦截。


来源:Hacker News · blog.gitguardian.com