随着智能体能力不断增强,其潜在的破坏范围也在扩大。工程上的问题在于如何限制这一范围。以下是我们为 claude.ai、Claude Code 和 Cowork 构建防护机制所积累的经验。
十二个月前,我们绝不会考虑授予 Claude 足以摧毁 Anthropic 内部服务的访问权限。如今,这种级别的访问权限已是常态,Anthropic 的开发者也因此效率更高。这些部署的风险包含两个部分:故障发生的可能性,以及故障可能造成的损害程度。安全防护措施和模型训练的进步已稳步降低了前者;而后者——理论上的破坏范围——则随着能力和访问权限的扩展而不断增大。然而,当智能体能够完成曾经需要一个人甚至一个团队才能完成的工作时,不部署的成本就会变得足够高,以至于只要产品能够确保安全,风险收益的计算就会强烈倾向于部署。工程问题就变成了如何限制破坏范围。

实现这一目标大致有两种方法。
第一种方法是通过人工介入来监督智能体的行为。Claude Code 此前通过在每个操作环节请求用户许可,来防止智能体执行非预期操作。理论上这行得通,但我们发现这种方法存在缺陷。我们的遥测数据显示,用户批准了约 93% 的许可提示。用户看到的批准请求越多,对每个请求的关注就越少,久而久之,他们在监督上的认真程度就会大大降低。我们最近构建了 Claude Code 自动模式,该模式能自动处理更安全的批准,以减轻这种批准疲劳。不过,漏洞依然存在——任何概率性防御都存在非零的漏报率。
限制影响范围的第二种方法——也是本文重点讨论的内容——是隔离。我们不监督智能体做什么,而是通过实施访问边界来监督它能做什么,例如通过沙箱、虚拟机和出口控制。这是 Anthropic 工程团队投入精力最多的地方,也是许多最令人意外的安全故障发生之处。
在过去两年里,我们发布了三款主要的智能体产品:claude.ai、Claude Code 和 Claude Cowork。每款产品面向不同的用户群体,需要不同的隔离架构。本文分享了哪些方案行之有效、哪些方案出了问题,以及我们在此过程中学到的关于智能体安全的知识。
三类风险,三种防御组件
智能体面临的安全风险可分为以下三类:
用户滥用:用户——无论是出于恶意还是疏忽——指示智能体执行有害操作。这包括从要求智能体绕过他们认为烦人的检查,到运行他们自己都不理解的破坏性命令,再到蓄意造成伤害等种种情况。
模型行为异常:智能体在无人要求的情况下采取了有害行动。随着模型能力的提升,它们在大多数行为评估中变得更加对齐,但这并不意味着风险必然降低。能力较弱的模型更容易误判情境并犯下明显错误。能力更强的模型犯错更少,但它们也更善于找到通往目标的意外路径,通常是通过绕过那些没人想到要写下来的限制条件。
在 Anthropic,我们曾看到 Claude 模型“好心”地逃出沙箱以完成任务,检查 git 历史记录以寻找编程测试的答案,以及自发识别出它正在运行的基准测试,以便解密其答案密钥。每个模型都带来了一套新的能力,而这些能力有时会以意想不到的方式被运用。
外部攻击者:智能体通过外部向量受到攻击,例如工具、文件或网络访问。此类别既包括提示词注入,也包括针对智能体运行时、编排层或代理的传统攻击。
在构建隔离与防御系统时,我们对三个主要组件应用防御措施:
智能体运行的环境。我们通过进程沙箱、虚拟机、文件系统边界和出口控制来约束智能体行动的地点和方式。目标是设定一个智能体能够触及的硬边界。例如,如果凭证从未进入沙箱,那么无论原因是用户、模型找到了“创造性”路径,还是攻击者,它们都无法被窃取。
紧密的边界也意味着你可以放宽监督。Claude Code 的参考开发容器正是为了让智能体能够在无人值守、无需逐操作审批的情况下运行。
智能体所咨询的模型。这里的机制包括系统提示词、分类器、探针和训练修改。由于模型是概率性的,这些机制只能塑造智能体倾向于做什么,而不能决定它在理论上能够做什么。
这些防御措施非常强大。在 Gray Swan 的 Agent 红队测试基准(该基准用于测试对提示词注入的敏感性)上,Claude Opus 4.7 将单次攻击的成功率控制在约 0.1%,在 100 次自适应攻击后也仅约 5–6%。Claude Code 自动模式能在约 83% 的过度激进行为执行之前将其捕获。然而,即使拥有业界领先的防御措施,模型层的保护也永远无法达到 100% 有效,这就是它不能单独发挥作用的原因。
智能体能够触及的外部内容。MCP 服务器、第三方插件和网络搜索工具都会将来自你无法控制的来源的内容输入到智能体的上下文中。经过审计的连接器并不等同于经过审计的数据——例如,一个 GitHub 连接器,尽管通过了恶意软件检查,却可能将一个被投毒的 README 直接加载到模型的上下文中。细粒度地限制工具权限有助于限制爆炸半径。例如,一个拥有数据库只读权限的智能体,其部署范围可以比一个拥有生产环境写入权限的智能体广泛得多。
防御措施应该相互重叠并互为补充。当环境层防御不可用时,模型层必须填补空缺(这正是 Claude Code 自动模式的设计目的)。在本地,环境层和模型层的防御可以防范恶意工具输出,但通过在更高层级限制工具的能力和访问权限,可以增加额外的防御。

约束智能体的模式
聚焦于环境层,我们描述了三种隔离模式,以及它们如何针对每个 Claude 平台——claude.ai、Claude Code 和 Cowork——进行定制。我们是在逐步探索中,在智能体所需能力与用户所需干预程度之间找到平衡后,才最终确定每种设计的。
模式 1:临时容器(claude.ai 代码执行)
尽管claude.ai最广为人知的是其聊天界面,但它也能编写并运行代码、生成文件以及调用连接器。当Claude在claude.ai内部运行代码时,它是在隔离基础设施上的gVisor容器中进行的。该智能体完全在服务端运行;没有任何代码在本地机器上执行,文件系统是临时的(按会话管理)。爆炸半径极小,但Claude能做的事情上限也很低——没有持久化工作空间,也无法访问用户的文件系统。
这也使得claude.ai面临更传统的威胁模型。我们不是在保护用户机器免受智能体侵害;而是在保护我们自己的基础设施以及各个租户之间相互隔离。我们在claude.ai上线前的主要工作集中在传统安全领域,如网络配置、内部服务认证和编排调度。
这项工作再次印证了安全领域最古老的教训:最薄弱的环节往往是你自己构建的那一层。gVisor和seccomp在智能体AI出现之前就已经针对资源充足的攻击者进行了长期加固,因此审查工作主要集中在我们围绕它们构建的新组件上。我们稍后会再回到这一点,因为在后果最严重的事故中,正是我们自定义的代理组件出了问题。
模式二:人在回路中的沙箱(Claude Code)
Claude Code运行在用户机器上,可以访问用户的文件系统、Shell和网络。没有这些权限,编码智能体的实用性将非常有限,因此必须找到一种安全授予这些访问权限的方法。
一种方法是依赖人在回路中。这对Claude Code来说是一个可行的解决方案,因为其典型用户是熟悉编码环境的开发者:他们能读懂bash命令,理解`rm -rf`的作用,并且每周都会从不可信来源运行`npm install`。这意味着,当弹出“允许此操作”的对话框时,他们极有可能具备准确评估智能体试图执行的操作及其风险的专业知识。基于此,Claude Code上线时采用了最简单的防御措施:允许读取,但写入、bash命令和网络访问需要用户批准。
然而,如前所述,批准疲劳在几周内就显现了。讽刺的是,这意味着原本旨在提供监督的功能,反而可能产生相反的效果——一些用户可能干脆不再关注。作为减少草率批准的第一步,我们部署了一个操作系统级别的沙箱(macOS 上使用 Seatbelt,Linux 上使用 bubblewrap)来强化边界:允许读取,允许在工作区内写入,但默认禁止网络连接。在沙箱内,智能体基本可以无中断地运行。结果,权限提示减少了 84%,并且我们将运行时开源,因此边界是可审计的。
我们的匿名使用数据还显示,经验丰富的用户自动批准的频率大约是新手用户的两倍,但他们也更频繁地在智能体执行过程中打断它。经验丰富的用户更倾向于只在智能体偏离轨道时才进行监督,而不是对每个步骤都加以限制。虽然这可能是人们与智能体协作方式的自然演变,但这种方式同样容易出错,它要求用户具备足够的技术能力和注意力,以便首先察觉到偏差。随着模型能力的提升,智能体开始编写越来越复杂的 bash 命令,察觉此类偏差也变得更加困难。而且,当用户转向多智能体系统时,这种方法作为有效的监督策略的可能性也大大降低。
我们遗漏的风险:信任对话框之前的一切
2025 年中至 2026 年 1 月期间,我们通过负责任披露计划收到了关于 Claude Code 的漏洞报告。其中三个漏洞针对的是在用户尚未同意任何操作之前就已执行的代码。要理解这是如何发生的,请考虑最直接的情况:开发者克隆一个仓库以审查拉取请求,而该仓库包含一个定义了钩子(hook)的 `.claude/settings.json` 文件。由于 Claude Code 在启动时会读取项目设置——在显示标准的“您是否信任此文件夹?”提示之前——攻击者编写并提交的钩子会自动执行。其余案例在结构上类似,即来自尚未受信任目录的输入在信任边界建立之前就被解析了。
每个案例的修复方案形式相同:将项目本地配置的解析和执行推迟到用户接受信任提示之后。如果你在构建类似的东西,请将项目打开、配置加载和 localhost 监听器视为来自互联网的入站请求。它们不应仅仅因为感觉像是本地操作且在用户同意之前到达就受到隐式信任。
我们遗漏的风险:用户作为注入向量
2026 年 2 月,在一次受控的内部红队演练中,一名研究人员成功通过钓鱼攻击诱使一名员工使用恶意提示词启动了 Claude Code。该钓鱼攻击看起来像普通的协作——一封“你能帮我运行一下这个吗?”的邮件附带了一个可直接粘贴的提示词——而该提示词本身读起来像是常规的任务指令。但在某些设置步骤中,它温和地要求 Claude 读取 `~/.aws/credentials`,对内容进行编码,并通过 POST 请求发送到一个外部端点。在对该提示词进行的 25 次重试中,Claude 完成了 24 次数据外泄。
这是一个直接的提示词注入——攻击者的指令是通过用户传入的,而非通过工具输出或获取的内容。我们的模型层防御机制以用户意图为锚点——当用户本人正在输入指令时,分类器没有任何异常可捕捉。一名人类合同工如果收到同样的脚本,也会做出同样的事情。
在这种情况下,唯一有效的防御手段是环境本身,具体而言就是出口控制(无论意图如何,都能阻止 POST 请求)和文件系统边界(从一开始就让 `~/.aws` 目录无法触及)。
(当我们在内部 Slack 上分享这个可运行的提示词进行讨论时,有人指出一些内部智能体会读取 Slack。此时载荷已经存在于环境中了。我们在讨论串中添加了一个金丝雀字符串,这样如果有人拾取它,我们就能察觉。在一个智能体读取一切的世界里,调查工具本身也是一个攻击面。)
模式 3:本地虚拟机(Claude Cowork)
Claude Cowork 运行在用户的桌面上,可以访问用户选定的工作区文件夹。由于该平台是为通用知识工作而非软件工程构建的,普通用户精通 bash 的可能性要低得多。
因此,人在回路中的沙盒策略可能无法沿用;不能指望非技术背景的知识工作者去判断诸如 `find . -name "*.tmp" -exec rm {} \;` 这样的 bash 命令。当批准例外情况需要普通用户不具备的专业知识时,管理员应该设定一个绝对且始终生效的边界。
为了实现这一点,我们的第一个 Claude Cowork 版本运行在一个完整的虚拟机内,使用的是平台供应商提供的虚拟机管理程序(macOS 上使用 Apple 的 Virtualization 框架,Windows 上使用 HCS)。该虚拟机拥有自己的 Linux 内核、自己的文件系统和自己的进程表。用户选定的工作区和 `.claude` 文件夹会被挂载;主机上的其他任何内容都不可见。凭据保留在主机钥匙串中,永远不会进入客户机。这种设计可以防范 Claude 在某个时刻出现行为失调的可能性。被攻破的 Claude 仍然可能损坏工作区文件夹内的内容,因此架构设计确保这是它唯一能够触及的范围(直到用户添加连接器),并且用户控制着挂载到那里的内容。
在最初的架构中——我们称之为全虚拟机模式——智能体循环本身运行在客户机内部,因此 Claude 以普通 Linux 用户的身份执行,完全不知道自身处于沙箱之中。相比之下,Claude Code 的做法是:一个特权进程位于沙箱外部,逐条指令决定是否强制执行;一条精心构造的注入提示词或一次疲劳状态下的批准点击,就可能让该进程运行未沙箱化的内容。而在这里,不存在持有逃生舱密钥的外部进程,因此没有任何组件有权授予例外。

然而,我们很快意识到,在全虚拟机模式下运行整个智能体会带来实际问题:虚拟机启动过程中的任何故障都会导致 Cowork 无法使用。将智能体循环移至虚拟机外部,同时将代码执行保留在虚拟机内部,使得 Claude 仍能响应用户并协助排查问题,而不是卡在错误上。这一变更对安全性的影响极小,因为虚拟机仍对智能体执行的代码实施文件系统和网络控制。
另外,我们还将本地 MCP 服务器移到了虚拟机外部。在虚拟机内部运行它们会导致审计困难、虚拟机更新时产生脆弱的依赖问题,并且无法支持需要与本地进程(如数据库)交互的 MCP——这类服务器无论如何都必须运行在宿主机上。这一变更使 Claude Cowork 与本地 MCP 服务器在 Claude Desktop 中的工作方式保持一致:将其视为用户可能选择安装的任何软件,并由管理员决定启用哪些本地 MCP(如果有的话)。远程 MCP 服务器不受影响,因为它们不在用户机器上运行。

文件系统控制是另一项重要的架构选择。Claude 需要能够访问主机上的某些文件才能发挥作用,但我们希望将影响范围降至最低,并向用户提供本地文件访问的透明度。我们发现,提供不同的文件挂载模式有助于精细控制风险;Claude Cowork 提供了只读、读写和读写-禁止删除三种模式。这里一个潜在的陷阱是,符号链接解析必须在路径验证之前进行,而不是之后,否则授权文件夹内的符号链接可能指向外部并造成逃逸。对于企业客户,我们允许管理员通过 MDM 设置中的挂载路径白名单来控制这一点。
我们遗漏的风险:通过已批准域名的数据外泄
一个通过已批准域名进行数据外泄的典型案例来自第三方披露。Claude Cowork 的出站白名单正确地将流量放行至 api.anthropic.com——产品离不开调用我们自己的 API。在这个案例中,一个放置在用户已挂载工作区中的恶意文件,携带了隐藏指令以及攻击者控制的 API 密钥。Claude 按照指令读取了工作区中的其他文件,并使用攻击者的密钥调用了 Anthropic 的 Files API。出站代理检查了目标地址,看到是 api.anthropic.com,便予以放行。文件被上传到了攻击者的 Anthropic 账户。沙箱运行完美,但数据仍然被外泄了。
此前,我们将白名单概念化为一个目标过滤器,它告诉 Claude 这些域名是可以通信的。但或许将其概念化为一种能力授权更为恰当。白名单上任何域名所能触及的每一个功能,现在都成为了攻击面。允许 api.anthropic.com 就意味着允许向任意 Anthropic 账户上传文件。
我们通过在虚拟机内部部署一个防御性中间人代理来修复此问题,该代理会拦截发往我们 API 的流量。它只允许携带虚拟机自身配置的会话令牌的请求通过;攻击者嵌入的密钥会被代理拒绝。同时,该代理还会阻止那些可能启用服务端 fetch 的请求头。这个代理位于虚拟机内部而非我们的服务器上,因为只有虚拟机才知道请求的来源——从服务器的角度来看,一个 Cowork 请求与任何其他 API 客户端是无法区分的。

这也再次印证了一个原则:你自己构建的软件往往是最薄弱的环节。我们各产品中的虚拟机监控器、seccomp 和 gVisor 一直都很可靠。而失败的是我们自定义的允许列表代理。
我们忽略的风险:虚拟机隔离同样将端点检测软件拒之门外
在评估 Claude Cowork 时,企业安全团队问道:“为什么我们的 EDR 无法看到内部?”答案是,用于隔离 Claude 的同一套机制,也阻止了基于主机的端点检测与响应。从 EDR 的视角来看,Claude Cowork 是一个不透明的虚拟机监控器进程,它无法检查客户机内部。
隔离降低了可见性,而对于那些合规性依赖于端点可见性的团队来说,不透明性是个问题。我们目前的缓解措施是使用基于拉取的 OTLP 导出,让管理员可以在事后检索事件日志,但这与实时监控不同。如果你在构建类似的东西,请尽早为这类讨论做好准备。
| 环境 | 临时容器 (claude.ai) | 人工介入沙箱 (Claude Code) | 密封虚拟机 (Claude Cowork) |
|---|---|---|---|
| 成本:隔离开销 | 容器启动 | 低延迟原生沙箱 | 完整虚拟机启动 |
| 成本:用户依赖 | 不适用 | 必须解释 bash | 不适用 |
| 风险:爆炸半径 | 服务端容器(由 gVisor + 主机基础设施边界防护) | 本地工作区 | 挂载的工作区(由 vsock + 虚拟机监控器边界防护) |
信任智能体所读取的内容
企业经常问我们如何保护 MCP 连接的安全。这是个好问题,但更准确的问题其实比 MCP 本身更广泛。提供给智能体的任何外部资源都同时带来两种风险:传统供应链意义上的代码执行风险,以及提示词注入攻击向量。传统的依赖审计(锁定版本、验证签名、审查源代码)解决了第一种风险,但忽略了第二种。
远程与本地之间的区别比表面看起来更重要。本地安装的工具是可审计的。你可以阅读代码、锁定版本,并且知道它不会在你不知情的情况下发生变化。而远程工具——托管的 MCP 服务器、云连接器——在你批准之后,随时可能改变行为;你安装时的信任决策可能不再适用。我们的连接器目录通过持续审查来解决这个问题,但目录之外的任何内容都应被视为不可信。应首先在恶意工具破坏范围可控的环境中,用假数据对其进行测试。
即使工具本身是可信的,工具输出也是一个攻击面。前面提到的 GitHub README 示例正是这种情况;应用于网页的任何输入扫描,都需要以同样严格的标准应用于支持网络的工具结果。尽管这会增加延迟并且不是完美的防御手段,但我们倾向于进行实时检查:一旦被投毒的工具返回结果引导智能体窃取数据,日志中只会显示一次成功且经过授权的 API 调用。事后没有任何信号可供追查。
在 Claude Code 和 Claude Cowork 中,工具调用会通过代理进行路由,这些代理强制执行网络和文件策略,并且可以在返回值进入模型上下文之前对其进行检查。执行检查的分类器可以是一个小型、快速的模型;它不需要是执行推理的那个模型。
展望未来
模型和产品正在快速发展。随着这种发展,风险也在不断演变和变化,我们的缓解措施必须跟上步伐才能应对这些风险。
持久性记忆投毒。跨会话持续存在的智能体上下文占比不断增长——这包括产品记忆、CLAUDE.md 文件、挂载的工作区,以及定时运行和长期运行的智能体的状态目录。任何注入到这些位置的恶意内容,都会在智能体每次启动时被重新加载。随着越来越多的智能体状态在会话结束后得以保留,我们正面临经典后利用意义上的新型持久化机制威胁。会话启动时的良好分类器将需要变得更加普及。
多智能体信任升级。一方面,子智能体可以隔离不受信任的内容,向主智能体返回结构化事实而非原始文本。另一方面,这也可能被滥用:如果子智能体的输出因其来自“我们”而被视为比原始工具结果具有更高信任度,就会引入一种新的提示词注入向量。在多智能体系统中,分配不同信任级别与容易遭受信任升级之间存在权衡。
智能体身份。Claude Cowork 对智能体身份的解决方案是具体的:凭据保留在主机钥匙串中,虚拟机获得一个按会话限定的令牌,并且该令牌可以独立于用户的令牌被撤销。然而,我们开始着手应对跨平台智能体身份这一更广泛的问题。智能体应该拥有自己的主体身份,还是应该作为用户的扩展并继承用户的权限?最终,答案可能是两者的结合。
随着智能体能力不断增强,攻击面也在持续变化。我们所见过的各类故障类型,很可能在整个行业和实验室中重复出现。我们需要在智能体专属安全态势方面进行集体投入,从共享基准和披露规范,到通用身份标准和跨厂商红队演练。本文重点讨论的是隔离管控,但这只是智能体安全图景的一部分。关于治理、可观测性及其他技术栈内容,请参阅 NIST 的 AI 智能体身份与授权项目、由澳大利亚 ACSC 牵头、CISA 和英国 NCSC 参与的六机构智能体 AI 采用指南,以及 AI 管理标准 ISO/IEC 42001。我们的 Glasswing 计划只是其中一项贡献,但我们期待与合作伙伴和竞争对手共同应对这一关键问题。
总结
简而言之,我们反复遵循以下几个原则:
首先在环境层设计隔离管控,然后在模型层引导行为。让我们学到最多的两起事件——员工钓鱼事件和第三方白名单泄露事件——都属于数据外泄,即数据通过允许的路径流出。在这两起事件中,模型层无能为力,没有任何异常可供其捕捉。当所有概率性手段都失效时,最终起作用的正是确定性边界。
将隔离强度与用户的监督能力相匹配。能读懂 bash 的开发者与读不懂的知识工作者,所面对的威胁模型完全不同。用户能否评估智能体即将执行的操作,这个问题应有助于确定隔离策略;而无论哪个方向判断错误——对专家设置过多障碍,或对非专家给予过多信任——本身就是一种失败。
警惕自定义组件。经过实战考验的虚拟机监控器、系统调用过滤器和容器运行时,所承受的对抗性关注远超你自己构建的任何东西。在本文描述的每一个部署场景中,标准原语都保持了稳定,而我们围绕它们自行构建的工作反而暴露了缺陷。
归根结底,尽管智能体可能是一种新型软件,但其系统层面的交互方式并非全新。它们仍然会读取文件、打开网络套接字和创建进程;这使得利用成熟的工具进行隔离成为一项至关重要的可行防御措施。随着人工智能的发展,部署的风险收益平衡会不断变化,但对爆炸半径设置硬性限制,往往能迫使这种平衡朝着正确的方向发展。
致谢
本文由 Max McGuinness、Mikaela Grace、Jiri De Jonghe、Jake Eaton 和 Abel Ribbink 撰写。
我们还要感谢 Hanah Ho、Hasnain Lakhani、Pedram Navid、Molly Villagra、Maya Nielan、Akila Srinivasan、Travis Szucs、Sam Attard、Alfred Xing、Mohamad El Hajj、Gabby Curtis、David Dworken、Adam Jones、Amie Rotherham、Christian Ryan、Lucas Smedley、Brett Andrews 以及其他人的贡献。
特别感谢我们的安全和产品工程团队,以及那些向 Claude 产品报告漏洞的个人和组织。
脚注
- Claude Code 的自动模式将命令审批委托给基于模型的分类器;它最大限度地减少了操作摩擦(大约阻止了 0.4% 的良性命令),但代价是会遗漏一小部分有风险的操作(大约 17% 的过度操作会通过),因此它只是沙箱内纵深防御体系中的一层,而非其替代品。
As agents grow more capable, so does their potential blast radius. The engineering question is how to cap it. Here’s what we’ve learned building containment for claude.ai, Claude Code, and Cowork.
Twelve months ago, we'd have rejected out of hand the idea of granting Claude access sufficient to take down an internal Anthropic service. Today that level of access is routine, and Anthropic developers are more productive for it. The risk of these deployments has two components: how likely a failure is, and how much damage one could do. Progress on safeguards and model training has steadily driven down the first; the second—the theoretical blast radius—only grows as capabilities and access expand. Yet as agents become capable of doing work that once required a person or even a team, the cost of not deploying grows large enough that the risk-reward calculation tips heavily toward adoption, as long as products can be made safe. The engineering question becomes how to cap the blast radius.

There are broadly two ways to do this.
The first is to supervise the agent’s behavior via a human-in-the-loop. Claude Code previously protected against agents taking unintended actions by asking users for permission at each turn. Theoretically that works, but we’ve found the approach to be fallible. Our telemetry showed users approved roughly 93% of permission prompts. The more approvals a user sees, the less attention they pay to each, becoming over time much less diligent in their supervision. We recently built Claude Code auto mode, which automates safer approvals in order to reduce this approval fatigue. Still, vulnerabilities remain—any probabilistic defense has a non-zero miss rate.1
The second approach to capping the blast radius—and the focus of much of this post—is containment. Rather than supervising what the agent does, we supervise what it’s able to do by enforcing access boundaries through, for example, sandboxes, virtual machines, and egress controls. This is where Anthropic engineering has devoted the most effort, and also where many of the most surprising security failures have occurred.
Over the past two years, we’ve shipped three primary agentic products: claude.ai, Claude Code, and Claude Cowork. Each serves a different audience, requiring a different containment architecture. This article shares what’s held up, what’s broken, and what we’ve learned about agent security along the way.
Three types of risk, three components of defense
Security risks to agents fall into one of three categories:
User misuse: A user—either maliciously or through carelessness—directs the agent to do something harmful. This includes everything from asking the agent to bypass a check they find annoying, to running a destructive command they don’t understand, to specifying intentional harm.
Model misbehavior: The agent takes a harmful action no one asked for. As our models have improved, they have become more aligned on most behavior evaluations, but this doesn’t mean risk necessarily shrinks. Less capable models are more likely to misread a situation and make obvious errors. More capable models make fewer mistakes, but they’re also better at finding unexpected paths to a goal, often by routing around restrictions nobody thought to write down.
At Anthropic, we’ve seen Claude models “helpfully” escape a sandbox in order to complete a task, examine git history to find answers to a coding test, and spontaneously identify the benchmark it was being run on in order to decrypt its answer key. Each model brings a new set of capabilities that are sometimes put to work in unexpected ways.
External attackers: The agent is attacked through external vectors such as tools, files, or network access. This category includes both prompt injection and conventional attacks on the agent's runtime, orchestration layer, or proxy.
When building containment and defense systems, we apply defenses to three main components:
The environment in which the agent runs. We constrain where and how an agent can act with process sandboxes, VMs, filesystem boundaries, and egress controls. The goal is to set a hard boundary on what an agent can reach. For example, if credentials never enter the sandbox, they can't be exfiltrated, regardless of whether the cause is a user, a model finding a “creative” path, or an attacker.
A tight perimeter also means you can relax oversight. Claude Code’s reference devcontainer exists precisely so that the agent can run unattended, without per-action approvals.
The model the agent consults. The mechanisms here include system prompts, classifiers, probes, and training modifications. Because models are probabilistic, these shape only what the agent tends to do, not what it is theoretically capable of doing.
These defenses are strong. On Gray Swan's Agent Red Teaming benchmark, which tests susceptibility to prompt injection, Claude Opus 4.7 holds attack success to roughly 0.1% on single attempts, and around 5–6% after 100 adaptive attempts. Claude Code auto mode catches roughly 83% of overeager behaviors before they execute. Yet even with best-in-class defenses, protection in the model layer will never be 100% effective, which is why it can't stand alone.
The external content the agent can reach. MCP servers, third-party plugins, and web search tools all feed content into the agent’s context from sources you don’t control. An audited connector isn’t the same as audited data—a GitHub connector, for instance, can load a poisoned README straight into the model’s context despite passing malware checks. Granularly limiting tool permissions can help limit the blast radius. An agent with read-only DB access, for instance, can be deployed far more broadly than one that writes to prod.
Defenses should overlap and complement each other. When environmental defenses aren’t available, the model layer has to pick up the slack (this is precisely what Claude Code’s auto mode is designed for). Locally, the environment and model defenses can guard against malicious tool outputs, but defenses can be added higher up the chain by limiting the tool’s capabilities and access.

Patterns for containing agents
Focusing on the environment layer, we describe three isolation patterns and how they’re tailored for each Claude platform—claude.ai, Claude Code, and Cowork. We arrived at each design gradually, after finding the balance between the capabilities we need from the agent and the degree of intervention required from the user.
Pattern 1: The ephemeral container (claude.ai code execution)
Though best known as a chat interface, claude.ai also writes and runs code, generates files, and calls connectors. When Claude runs code inside claude.ai, it does so in a gVisor container on isolated infrastructure. The agent is entirely server-side; no code runs on the local machine, and the filesystem is ephemeral (per-session). The blast radius is minimal, but so is the ceiling on what Claude can do—there's no persistent workspace and no access to the user's filesystem.
This also makes claude.ai subject to a more traditional threat model. We're not protecting user machines from agents; we're protecting our own infrastructure and each tenant from one another. Our pre-launch work for claude.ai was dominated by traditional security work like network configuration, internal service auth, and orchestration.
That work reinforced the oldest lesson in security: the weakest layer is the one you built yourself. gVisor and seccomp have been hardened against well-resourced adversaries for far longer than agentic AI has existed, so the review effort went into the newer pieces we'd built around them. We’ll come back to this later, since our custom proxy is also the piece that broke in our most consequential incident.
Pattern 2: The human-in-the-loop sandbox (Claude Code)
Claude Code runs on a user's machine and has access to their filesystem, shell, and network. Without this, coding agents have limited usefulness, so it’s imperative to find a way to grant that access safely.
One approach is to rely on a human-in-the-loop. This is only a tractable solution for Claude Code because the average user is a developer who’s familiar with coding environments: they can read bash, they understand what rm -rf does, and they already run npm install from untrusted sources several times a week. All that means that when an “allow this” dialog pops up, they are highly likely to have the expertise to accurately evaluate what the agent is attempting to do and the risk involved. Given this, Claude Code launched with the simplest possible defense: allow reads, require approval for write, bash, and network access.
However, as mentioned, approval fatigue showed up within weeks. Ironically, this meant that a feature originally designed to provide oversight could arguably have the opposite effect—some users might simply stop paying attention. As a first step to mitigate incautious approvals, we shipped an OS-level sandbox (Seatbelt on macOS, bubblewrap on Linux) that hardens the boundary: reads are allowed, writes are allowed inside the workspace, but network is denied by default. Within the sandbox, the agent runs largely without interruption. The result was an 84% reduction in permission prompts, and we open-sourced the runtime, so the boundary is auditable.
Our anonymized usage data also showed that experienced users auto-approve roughly twice as often as new users, but they also interrupt the agent mid-execution more frequently. Instead of gating individual steps, experienced users are more likely to supervise the agent only when it goes off track. While this may be a natural evolution in how people prefer to work with agents, this too is fallible, requiring users to be technical and attentive enough to notice drift in the first place. As model capabilities improve and agents begin writing increasingly ambitious bash, it becomes harder to notice any such drift. And as users move to multi-agent systems, this approach is also much less likely to be an effective oversight strategy.
Risk we missed: Everything before the trust dialog
Between mid-2025 and January 2026, we received reports of vulnerabilities in Claude Code through our responsible disclosure program. Three of these vulnerabilities targeted code that executes before the user has consented to anything. To understand how this is possible, consider the most direct case: a developer clones a repository to review a pull request, and that repository contains a .claude/settings.json which defines a hook. Because Claude Code reads project settings during startup—before presenting the standard "Do you trust this folder?" prompt—the hook the attacker had authored and committed would execute automatically. The remaining cases looked structurally similar, in which input from the not-yet-trusted directory was parsed before the trust boundary had been established.
The fix in each case had the same shape: defer parsing and execution of project-local configuration until after the user accepts the trust prompt. If you're building something similar, treat project-open, config-load, and localhost listeners the way you'd treat any inbound request from the internet. They shouldn’t be implicitly trusted just because they feel local and arrive before the user has consented.
Risk we missed: The user as an injection vector
In February 2026, during a controlled internal red-team exercise, a researcher successfully phished an employee into launching Claude Code with a malicious prompt. The phish looked like ordinary collaboration—a "can you run this for me?" email with a ready-to-paste prompt attached—and the prompt itself read like routine task instructions. But somewhere among the setup steps, it gently asked Claude to read ~/.aws/credentials, encode the contents, and POST them to an external endpoint. Across 25 retries of that prompt, Claude completed the exfiltration 24 times.
This is a direct prompt injection—the attacker's instructions arrived through the user, not through tool output or fetched content. Our model-layer defenses anchor on user intent—when the user is the one typing the instruction, there's nothing anomalous for a classifier to catch. A human contractor handed the same script would have done the same thing.
The only defense that holds in this situation is the environment, specifically egress controls that block the POST regardless of intent and filesystem boundaries that keep ~/.aws out of reach in the first place.
(When we shared the working prompt in internal Slack for discussion, someone pointed out that some internal agents read Slack. The payload was now ambient. We added a canary string to the thread so we'd notice if anything picked it up. In a world where agents read everything, the investigation tooling is also an attack surface.)
Pattern 3: The local VM (Claude Cowork)
Claude Cowork runs on a user's desktop with access to a workspace folder selected by the user. Because the platform is built for general knowledge work, not software engineering, the average user is much less likely to be fluent in bash.
As a result, the human-in-the-loop sandbox strategy may not transfer; a non-technical knowledge worker shouldn’t be expected to judge bash incantations such as find . -name "*.tmp" -exec rm {} \;. When approving an exception requires expertise the typical user doesn’t have, admins should set a boundary that is absolute and always-on.
To enable this, our first version of Claude Cowork ran inside a full virtual machine using the platform's vendor hypervisor (Apple's Virtualization framework on macOS, HCS on Windows). The VM has its own Linux kernel, its own filesystem, and its own process table. The user's selected workspace and .claude folder are mounted; nothing else on the host is visible. Credentials stay in the host's keychain and never enter the guest machine. This design protects against the possibility that Claude will, at some point, behave in a misaligned manner. A compromised Claude could still damage what's inside the workspace folder, so the architecture is designed to make sure that's the only thing it can reach (until the user adds connectors), and that the user controls what's mounted there.
In the original architecture—what we call full-VM mode—the agent loop itself ran inside the guest, so Claude executed as an ordinary Linux user with no awareness it was sandboxed. Compare this to Claude Code, where a privileged process sits outside the sandbox deciding per-command whether to enforce it; a persuasive injected prompt or a fatigued approval click can get that process to run something un-sandboxed. Here, there was no outer process holding an escape-hatch key, and so no component with the authority to grant an exception.

However, we soon realized that running the whole agent in full-VM mode caused practical problems: any failure during VM startup made Cowork unusable. Moving the agent loop outside of the VM, while keeping code execution inside of it, allowed Claude to still respond to the user and help debug issues rather than freeze on an error. This change caused minimal security impact because the VM still enforces filesystem and network controls over code executed by the agent.
Separately, we also moved local MCP servers outside the VM. Running them inside the VM made them harder to audit, created brittle dependency issues when the VM updated, and didn’t support MCPs that required interaction with local processes such as databases—such servers had to run on the host regardless. The change brings Claude Cowork in line with how local MCP servers already work in Claude Desktop: treating them like any software a user might choose to install and entrusting admins to decide which local MCPs to enable (if any). Remote MCP servers are unaffected since they do not run on the user's machine.

Filesystem controls were another important architectural choice. Claude needs to be able to access some files on the host in order to be useful, but we wanted to minimize the blast radius and provide transparency to the user about local file access. We found that offering different file-mount modes helps to granularly control risk; Claude Cowork offers read-only, read-write, and read-write-no-delete. One potential gotcha here is that symlink resolution has to happen before path validation, not after, or a symlink inside an authorized folder can point outside and escape. For enterprise customers, we allow admins to control this via mount-path allowlists in MDM settings.
Risk we missed: Exfiltration through an approved domain
A clear example of exfiltration through an approved domain came from a third-party disclosure. Claude Cowork's egress allowlist correctly passed traffic to api.anthropic.com—the product can't function without calling our own API. In this case, a malicious file placed in the user's mounted workspace carried hidden instructions along with an API key controlled by the attacker. Claude, following the instructions, read other files in the workspace and called Anthropic's Files API using the attacker's key. The egress proxy checked the destination, saw api.anthropic.com, and let it through. The files were uploaded to the attacker's Anthropic account. The sandbox worked perfectly, and yet the data was exfiltrated.
Previously, we’d conceptualized the allowlist as a destination filter, something that told Claude these domains are okay to talk to. But it may be better conceptualized as a capability grant. Every function reachable through any domain on an allowlist is now an attack surface. Allowing api.anthropic.com meant allowing file uploads to arbitrary Anthropic accounts.
We fixed it using a defensive man-in-the-middle proxy inside the VM that intercepts traffic to our API. It only passes requests carrying the VM's own provisioned session token; an attacker-embedded key is rejected by the proxy. It also blocks headers that would enable server-side fetch. The proxy sits inside the VM rather than on our servers because only the VM knows provenance—from the server's perspective, a Cowork request is indistinguishable from any other API client.

This is also a second instance of the principle that the software you build yourself is often the weakest. The hypervisor, seccomp, and gVisor across our products have been dependable. Our custom allowlist proxy was the piece that failed.
Risk we missed: VM isolation kept the endpoint detection software out too
When evaluating Claude Cowork, enterprise security teams asked, "Why can't our EDR see inside?" The answer was that the same isolation keeping Claude contained also kept host-based endpoint detection and response out. From the EDR's perspective, Claude Cowork is an opaque hypervisor process. It can't inspect the guest.
Isolation reduces visibility, and opacity is problematic for teams whose compliance posture depends on endpoint visibility. Our current mitigation is to use pull-based OTLP exports that let administrators retrieve event logs after the fact, but this is not the same as live monitoring. If you're building something similar, budget for this conversation early.
| Environment | Ephemeral container (claude.ai) | HITL sandbox (Claude Code) | Sealed VM (Claude Cowork) |
|---|---|---|---|
| Cost: Isolation Overhead | Container spin-up | Low-latency native sandbox | Full VM boot |
| Cost: User Reliance | N/A | Must interpret bash | N/A |
| Risk: Blast Radius | Server-side container (guarded by gVisor + host infra boundary) | Local workspace | Mounted workspace (guarded by vsock + hypervisor boundary) |
Trusting what the agent reads
Enterprises often ask us how to secure MCP connections. It's a good question, but the right one is broader than MCP specifically. Any external resource provided to an agent represents two risks at once: a code execution risk, in the traditional supply-chain sense, and a prompt injection vector. Traditional dependency auditing (pinning versions, verifying signatures, reviewing source) addresses the first, but misses the second.
Remote versus local is more important than it seems. A locally installed tool is auditable. You can read the code, pin the version, and know it won't change under you. A remote tool—a hosted MCP server, a cloud connector—can change behavior at any point after you’ve approved it; your install-time trust decision may no longer apply. Our connector directory addresses this through ongoing review, but anything outside it should be treated as untrusted. Run it against fake data first, in an environment where the blast radius of a malicious tool is contained.
Tool output is an attack surface even when the tool is trusted. The GitHub README example mentioned earlier is exactly this case; any input scanning applied to web pages needs to be applied to network-enabled tool results with the same rigor. Even though this adds latency and isn't a perfect defense, we err toward live inspection: once a poisoned tool return has steered the agent into exfiltrating data, the log just shows a successful, authorized API call. There's no after-the-fact signal to find.
In Claude Code and Claude Cowork, tool calls route through proxies that enforce network and file policy and can inspect return values before they enter the model's context. The classifier that does the inspection can be a small, fast model; it doesn't need to be the one doing the reasoning.
Looking ahead
Models and products are advancing fast. As they do, risks morph and evolve, and our mitigations must keep pace to meet them.
Persistent memory poisoning. The share of agent context that persists across sessions keeps growing—this includes product memory, CLAUDE.md files, mounted workspaces, and the state directories of scheduled and long-running agents. An injection that lands in any of these is reloaded each time the agent starts. As more agent state survives the session, we are threatened by new persistence mechanisms in the classic post-exploitation sense. Good classifiers on session startup will need to become more commonplace.
Multi-agent trust escalation. On the one hand, sub-agents can isolate untrusted content, returning structured facts rather than raw text up to the main agent. On the other hand, this can be abused: if a sub-agent's output is treated as higher-trust than raw tool results, because such output came from “us,” a new vector for prompt injection is introduced. In multi-agent systems, there is a tradeoff between allocating differing trust levels and becoming liable to trust escalation.
Agent identity. Claude Cowork's answer to agent identity is concrete: credentials stay in the host keychain, the VM gets a per-session scoped-down token, and that token can be revoked independently of the user's. However, we are starting to grapple with the broader question of cross-platform agent identity. Should an agent possess its own principal identity, or should it act as an extension of the user and inherit the user’s permissions? Ultimately, the answer may be a blend of the two.
As agents grow more capable, attack surfaces are constantly shifting. The types of failures we’ve seen are likely to be repeated across industries and labs. We need collective investment in agent-specific security posture, from shared benchmarks and disclosure norms to common identity standards and cross-vendor red-teaming. We focus on containment in this piece, but that's only one part of the security picture for agents. For governance, observability, and the rest of the stack, see NIST's project on AI agent identity and authorization, the six-agency guidance on adopting agentic AI led by Australia's ACSC with CISA and the UK's NCSC, and ISO/IEC 42001, the AI management standard. Our Glasswing initiative is one contribution, but we look forward to working with both partners and competitors on this critical issue.
Summary
In short, there are a few principles we keep returning to:
Design for containment at the environment layer first, then steer behavior at the model layer. Two of the incidents that taught us the most—the employee phish and the third-party allowlist disclosure—were both cases of egress, in which data left through a permitted path. In each, the model layer couldn't help; there was nothing anomalous for it to catch. The deterministic boundary is what gets hit when everything probabilistic misses.
Match isolation strength to the user's capacity for oversight. A developer who can read bash and a knowledge worker who can't are not running the same threat model. The question of whether a user can evaluate what an agent is about to do should help determine the containment strategy, and answering it wrong in either direction—too much friction for experts, too much trust for non-experts—is its own failure.
Be wary of custom components. Battle-tested hypervisors, syscall filters, and container runtimes have survived more adversarial attention than anything you'll build. Across every deployment described here, the standard primitives held while our own work around them exposed flaws.
Ultimately, while agents may be a new category of software, their system-level interactions are not. They still read files, open sockets, and spawn processes; this makes containment with mature tooling a crucially viable defense. The risk-reward balance of deployments will keep shifting as AI develops, but placing a hard limit on blast radius often forces that balance into the right direction.
Acknowledgements
Written by Max McGuinness, Mikaela Grace, Jiri De Jonghe, Jake Eaton, and Abel Ribbink.
We're also grateful to Hanah Ho, Hasnain Lakhani, Pedram Navid, Molly Villagra, Maya Nielan, Akila Srinivasan, Travis Szucs, Sam Attard, Alfred Xing, Mohamad El Hajj, Gabby Curtis, David Dworken, Adam Jones, Amie Rotherham, Christian Ryan, Lucas Smedley, Brett Andrews, and others for their contributions.
Special thanks to our security and product engineering teams, and to the individuals and organizations that have reported vulnerabilities in Claude products.
Footnotes
- Claude Code auto mode delegates command approvals to a model-based classifier; it minimizes friction (roughly 0.4% of benign commands blocked) at the cost of missing a fraction of risky ones (~17% of overeager actions get through), so it's one layer of defense-in-depth inside a sandbox, not a substitute for one.