
1. 为什么加固这件事比搭出一个能跑的 Agent 更值得先做我见过太多人搭 AI Agent 的路径是这样的找个框架接上模型 API写几个工具函数跑通一个帮我查天气发邮件的 demo然后兴冲冲地把它接到真实环境里——本地文件系统、公司内网、数据库、甚至生产服务器。跑是能跑但问题在于这个 Agent 从诞生那一刻起就带着一把能开你家所有门的钥匙而你对它几乎没有设防。Harden Your AI Agent这个标题翻译过来就是给你的 AI Agent 做加固。加固不是加功能恰恰相反它是做减法、做隔离、做边界。一个能读文件、能执行命令、能访问网络的 Agent本质上就是一个拥有你部分权限的自动化程序。你让它读一个目录它可能顺手把整个 home 目录遍历一遍你让它执行一条命令它可能因为模型幻觉拼出一条rm -rf的变体你让它访问一个 URL它可能把本地敏感配置当成请求参数发出去。所以这篇内容要解决的核心问题是在 macOS 上如何把一个能力很强但边界模糊的 AI Agent关进一个可控的安全屋Safehouse里让它只能干你允许它干的事。关键词里的 Agent Safehouse、macOS、sandbox、配置指向的正是这条路线——用系统级的沙箱机制而不是靠提示词里写一句请不要乱来这种自欺欺人的方式。适合谁看三类人。第一类是自己搭过 Agent、跑通过 demo、现在想把它用到真实工作流里的开发者第二类是在团队里负责 Agent 部署、需要给同事一个能放心用的环境的技术负责人第三类是对 macOS 系统安全机制感兴趣、想搞明白 sandbox 到底怎么落地的人。不需要你是安全专家但需要你对命令行、文件权限、进程这些概念有基本认知。我自己的经历是早期我让一个 Agent 帮我整理项目文档结果它把.env文件的内容读出来当成上下文塞进了发给模型的请求里。虽然那次没造成实际损失但从那以后我就明白Agent 的安全问题不是会不会发生而是什么时候发生。加固这件事越早做成本越低。2. 先搞清楚 Agent 到底在哪些地方漏风在动手配置之前得先建立一个清晰的威胁模型。很多人一上来就抄配置结果抄完也不知道自己防住了什么、没防住什么。我习惯把 Agent 的风险面拆成四层从外到内分别是网络出口、文件系统、进程执行、凭据与密钥。这四层里任何一层没管住加固都是纸糊的。2.1 网络出口Agent 最容易偷偷说话的地方Agent 和普通程序最大的区别是它会把大量上下文发给外部模型服务。这意味着你的代码片段、文件内容、环境变量都有可能随着一次请求离开本机。风险点有三个一是 Agent 可能被诱导去访问恶意 URL比如你让它总结一个网页网页里藏着指令注入二是它可能把本地文件内容作为请求体发出去三是它可能访问你根本没打算让它碰的内网地址。在 macOS 上网络层的加固思路是默认拒绝、按需放行。你要能回答一个问题这个 Agent 到底需要访问哪些域名如果它只是调用某一家模型 API那就只放行那一个域名其余全部阻断。这比允许所有出站流量然后祈祷它别乱来要靠谱得多。2.2 文件系统读权限比写权限更危险大多数人担心的是 Agent 删文件所以重点防写。但实际经验告诉我读权限的泄露往往更隐蔽也更致命。写坏了你能立刻发现读走了你可能几个月都不知道。.ssh目录、.aws/credentials、.env、浏览器 cookie 数据库、钥匙串导出文件这些都是 Agent顺手一读就能拿到的高价值目标。macOS 的文件系统加固核心是最小可见范围。不要让 Agent 以你的用户身份运行并拥有整个 home 目录的读权限而是给它一个专门的工作目录只挂载它真正需要的那几个路径。这一点和 Linux 上的容器挂载思路是一致的只是 macOS 用的是 sandbox profile 而不是 namespace。2.3 进程执行能执行命令就等于能执行任何命令只要 Agent 有执行 shell 命令这个工具它理论上就能做你在这个用户下能做的一切。模型幻觉、提示注入、工具参数拼接错误任何一个环节出问题都可能变成一条危险的命令。加固的方向不是禁止执行命令那 Agent 基本就废了而是限制它能执行的命令集合以及执行时的权限上下文。一个实用的做法是把 Agent 的命令执行能力收敛到几个白名单脚本上而不是给它一个通用的bash -c。比如你允许它整理文件那就提供一个organize.sh内部逻辑你写死Agent 只能传参数不能自己拼命令。这样即使模型被注入它能造成的破坏也被限制在这个脚本的能力范围内。2.4 凭据与密钥Agent 不该看见的东西就别让它看见这一层最容易被忽略。很多人把 API key 放在环境变量里然后 Agent 继承了这个环境于是它执行env就能看到所有密钥。更糟的是有些 Agent 会把环境变量作为上下文的一部分发给模型。加固的原则很简单Agent 进程的环境变量应该是干净的只包含它运行必需的那几个其余一律不注入。下面这张表是我总结的四层风险面和对应的加固手段可以先建立一个整体印象风险层典型泄露/破坏方式加固手段优先级网络出口上下文外发、访问恶意 URL、探测内网出站域名白名单、阻断内网网段高文件系统读取密钥文件、遍历 home 目录、误删独立工作目录、路径白名单、只读挂载高进程执行任意命令执行、提权、持久化命令白名单、降权运行、禁用危险系统调用中高凭据密钥环境变量泄露、配置文件被读干净环境、密钥按需注入、运行时短期凭据高把这张表想清楚后面的配置才有方向。接下来进入实操。3. 在 macOS 上给 Agent 搭一个 Safehouse 的完整路径macOS 的沙箱机制sandbox底层是sandbox-exec和一套 profile 描述语言虽然苹果官方对它的文档不多但它是系统自带、无需额外安装、且足够强力的隔离手段。围绕它社区里出现了像 Agent Safehouse 这类专门为 AI Agent 设计的封装方案把复杂的 profile 编写简化成可配置的规则。这一章我按从零到能跑的顺序讲每一步都说明为什么这么做。3.1 环境准备确认你的 macOS 版本和工具链第一步不是写配置而是确认环境。sandbox-exec在 macOS 上是长期存在的但不同版本行为有差异尤其是较新的系统对某些操作的默认限制更严。先跑一条命令确认它可用which sandbox-exec sandbox-exec -h如果能看到用法输出说明基础能力在。接着确认你的 Agent 是用什么语言/运行时写的这决定了后面 profile 里要放行哪些系统调用。比如 Node.js 写的 Agent 需要访问大量动态库和临时目录Python 写的则对site-packages路径有依赖。我建议先用一个最小可运行版本跑起来再逐步收紧而不是一上来就写一个完美的 profile——因为太严的 profile 会让 Agent 直接起不来你连调试都无从下手。提示不要在生产或主力工作机上第一次试 sandbox profile。用一个专门的测试用户账号或者至少在一个独立的项目目录里做避免配置错误影响你日常的文件操作。3.2 建立独立工作目录给 Agent 一个自己的房间这是整个加固里性价比最高的一步。创建一个专用目录比如~/agent-workspaceAgent 的所有读写都限制在这里。它看不到你的~/Documents、~/Downloads、~/.ssh自然也就无从泄露。mkdir -p ~/agent-workspace/{input,output,tmp} chmod 700 ~/agent-workspacechmod 700保证只有当前用户能进。然后关键的一步在 sandbox profile 里只允许 Agent 访问这个目录其余路径一律拒绝。这里有个细节——很多运行时需要访问/tmp或系统缓存目录如果你全禁了程序会报一堆莫名其妙的错。所以正确做法是允许访问系统必需的只读路径拒绝所有用户数据路径而不是一刀切全禁。3.3 编写 sandbox profile从全拒绝开始加白名单sandbox profile 的核心思想是(deny default)也就是默认拒绝一切然后逐条allow。这跟防火墙规则是一个逻辑。下面是一个简化后的骨架展示结构(version 1) (deny default) ;; 允许基本的进程操作 (allow process-fork) (allow process-exec) ;; 只读访问系统库和运行时 (allow file-read* (subpath /usr/lib) (subpath /System/Library) (subpath /opt/homebrew/lib)) ;; 读写限定在工作目录 (allow file-read* file-write* (subpath /Users/yourname/agent-workspace)) ;; 网络只放行模型 API 域名 (allow network-outbound (remote tcp api.example-model.com:443))这段配置的每一行都有意图deny default是底线file-read*对系统库是只读放行否则运行时起不来工作目录是读写放行网络只开一个出口。你要根据自己的 Agent 实际依赖去调整但结构一定是默认拒绝精确放行绝不能反过来。3.4 用 Agent Safehouse 简化配置管理手写 profile 的问题是难维护、易出错尤其是当你有多个 Agent、每个依赖不同的时候。Agent Safehouse 这类工具的价值就在于把 profile 抽象成配置文件让你用更接近自然语言的方式描述允许什么然后自动生成对应的 sandbox 规则。它的典型用法是在项目里放一个配置文件声明工作目录、允许的域名、允许的命令然后用它提供的命令启动 Agent。这样配置和代码一起进版本管理谁改了什么一目了然。我自己的习惯是给每个 Agent 单独一个配置文件命名成agent-name.safehouse.toml之类启动脚本里引用它。这样换机器、换同事复制配置就能复现同样的隔离环境。注意无论用不用封装工具都要理解底层 profile 在做什么。工具只是帮你生成规则规则本身的逻辑你得懂否则出了问题你连从哪查都不知道。3.5 启动与验证确认隔离真的生效配置写完最重要的一步是验证。不要假设它生效了要主动去测。我常用的验证清单是这样的让 Agent 尝试读取~/.ssh/id_rsa应该失败。让 Agent 尝试写入工作目录外的文件应该失败。让 Agent 尝试访问一个未在白名单里的域名应该失败。让 Agent 正常完成一次任务应该成功。只有这四条都符合预期才算配置正确。任何一条不符合都要回去查 profile。这一步很多人跳过结果以为自己加固了实际上规则写错了根本没生效。4. 配置里那些看起来对、实际会翻车的细节profile 写对了能跑不代表写对了就安全。这一章讲的是我在实际配置中踩过的坑以及那些文档里不会写、但会让你加固形同虚设的细节。这些经验的价值往往比配置本身更高。4.1 路径通配符的陷阱subpath和literal的区别sandbox profile 里路径匹配有好几种写法最容易搞混的是subpath和literal。subpath匹配某个目录及其所有子内容literal只匹配精确路径。我曾经写过一条(allow file-read* (literal /Users/me/agent-workspace))本意是允许访问工作目录结果 Agent 连目录里的文件都读不了——因为literal只匹配那个目录本身不匹配里面的文件。正确写法是subpath。但反过来如果你只想放行某一个具体文件比如某个配置文件用literal更安全因为subpath会把整个目录树都放开。放行范围能小则小这是加固的第一原则。4.2 符号链接绕过Agent 可能曲线访问禁区这是一个很隐蔽的坑。假设你禁止访问/etc但工作目录里有一个指向/etc的符号链接Agent 通过这个链接就能读到/etc下的内容。sandbox 默认对符号链接的处理取决于具体规则很多情况下它会跟随链接导致你的路径限制被绕过。防御方法是在 profile 里对敏感路径同时用subpath和literal双重拒绝并且在工作目录里定期检查有没有可疑的符号链接。更彻底的做法是让 Agent 运行在一个不包含任何外部链接的干净目录里从源头上杜绝。4.3 环境变量泄露env命令就是一次信息泄露前面提过Agent 继承的环境变量是个大问题。我实测过一个场景Agent 执行env命令把输出作为调试信息发给了模型结果里面包含了数据库连接串。虽然那次是测试环境但足以说明问题。加固做法是启动 Agent 时用一个干净的环境env -i HOME/Users/yourname/agent-workspace \ PATH/usr/bin:/bin:/usr/sbin:/sbin \ MODEL_API_KEYxxx \ your-agent-commandenv -i清空所有继承的环境变量只保留你显式指定的。这样即使 Agent 执行env看到的也只有这几个。密钥按需注入用完即弃不要长期挂在环境里。4.4 网络白名单的粒度域名 vs IP vs 端口网络放行的粒度选择直接影响安全性。只写域名Agent 可能通过 DNS 解析到别的 IP只写 IP域名换了就失效。我的建议是域名端口一起限定比如(remote tcp api.example-model.com:443)这样既限定了目标又限定了协议端口。同时在 profile 里显式拒绝内网网段如10.0.0.0/8、192.168.0.0/16防止 Agent 被诱导去探测内网服务。4.5 日志与审计出了问题要能查加固不只是防住还包括出了事能追溯。sandbox 可以配合系统日志记录被拒绝的操作。我习惯在测试阶段打开详细日志观察 Agent 到底尝试访问了哪些路径、哪些域名然后据此调整白名单。这个观察-调整的循环比一次性写死配置要科学得多。上线后再把日志级别调低避免性能开销。下面这张表汇总了这几个坑和对应的解法方便对照排查坑点表现根因解法路径通配符误用目录能进但文件读不了literal不匹配子内容改用subpath范围按需收窄符号链接绕过禁区被间接访问链接被跟随双重拒绝清理链接环境变量泄露env输出敏感信息继承了父进程环境env -i干净启动网络白名单过粗能访问非预期地址只限域名不限端口域名端口拒绝内网无审计日志出问题无法定位未开启日志测试期开详细日志5. 把加固做成流程而不是一次性动作配置写完、验证通过很多人就以为万事大吉了。但 Agent 是会变的——你会给它加新工具、接新服务、改依赖。每一次变更都可能打破原有的隔离边界。所以加固必须是一个持续的过程而不是一次性的配置任务。5.1 变更即审查新增工具时的检查清单每次给 Agent 加一个新工具或新依赖都要过一遍检查清单这个工具需要访问哪些新路径需要访问哪些新域名需要执行哪些新命令把这些增量加到 profile 里而不是图省事直接放宽整体规则。我见过太多人因为加个功能太麻烦直接把deny default改成allow default然后整个加固就废了。5.2 定期回归测试隔离规则也会腐烂系统更新、依赖升级、路径变化都可能让原本生效的规则失效。我建议每个月跑一次前面说的四条验证清单确认隔离依然有效。这个成本很低但能避免以为安全其实早就漏了的情况。5.3 分层防御sandbox 不是唯一手段sandbox 很强但不是万能。它主要防的是文件、网络、进程层面的越界。对于模型层面的提示注入、上下文泄露它无能为力。所以完整的加固应该是分层的sandbox 管系统资源输入输出过滤管内容密钥管理管凭据人工审核管高风险操作。任何一层单独拿出来都不够叠起来才稳。5.4 一个我常用的最小可用配置模板最后给一个我实际在用的最小模板你可以直接改成自己的路径和域名(version 1) (deny default) (allow process-fork) (allow process-exec) (allow file-read* (subpath /usr/lib) (subpath /System/Library) (subpath /opt/homebrew/lib) (subpath /opt/homebrew/bin)) (allow file-read* file-write* (subpath /Users/yourname/agent-workspace)) (allow network-outbound (remote tcp api.example-model.com:443)) (deny network-outbound (remote tcp 10.0.0.0/8:*) (remote tcp 192.168.0.0/16:*) (remote tcp 127.0.0.1:*))这个模板的思路是运行时只读放行、工作目录读写放行、网络只开一个出口、内网全部拒绝。你可以在此基础上按需增删但永远保持deny default在最上面。我在实际使用中最大的体会是加固带来的那点麻烦和一次泄露或误删造成的损失相比完全不值一提。刚开始配 profile 确实会花一两个小时调试但配好之后你就能放心地让 Agent 去干那些以前不敢让它碰的活。这种敢用的底气才是加固真正的价值。后续如果你要给 Agent 接入更多能力记得回到第 5 章那套流程把每一次变更都当成一次新的加固来做。