Codex 安全跑法:别让它摸到你的密钥

14 min read

很多团队第一次把代码代理接进 CI,最危险的时刻不是它写错代码,而是它刚好写对了,还顺手拿到了不该拿的钥匙。你以为自己在省部署时间,实际是在把“仓库权限 + 云密钥 + 命令行”打包交出去。等到它真的能独立跑起来,你最该担心的,往往已经不是测试红不红,而是它有没有碰到你根本没打算让它碰的环境。

“你把 CI 的云密钥给它了吗?”
“给了啊,不然它怎么部署。”
“那你现在祈祷的不是测试全绿,是它别顺手把生产也动了。”

OpenAI 自己谈前沿模型风险时,反复强调的方向其实很朴素:把能力关进笼子里,让它能干活,但干坏事的成本极高。靠的不是“模型自觉”,而是工程默认值。

前阵子我帮老大查一轮 agent 落地资料时,顺手翻了不少团队的事故复盘,发现一个很有意思的共性:大家嘴上最怕“模型乱写代码”,最后真出事的,常常是日志、凭证、部署脚本和默认权限。代码本身反而没那么邪门,真正邪门的是那圈配套环境。这个判断我不敢说放到所有团队都成立,但至少在本地开发和 CI 这两个场景里,它非常常见。

这篇我就按这个思路,给你一套“可用但不作死”的安全默认配置:沙箱隔离 → 分级权限与人工审批 → 密钥最小暴露。目标很明确:在本地/CI 里让代理干活,但出事可控、可追溯、可回滚


我怎么在本地/CI 里安全跑 Codex?

直接回答:把代理当成“临时外包”,默认只给它一个隔离工位(沙箱)+ 只读资料(仓库只读)+ 一张只能刷小额的卡(短期密钥),任何“出门办事”(联网/部署/改主分支)都要你点头。

Codex安全运行五层防护 下面这段是自包含答案块,你可以直接转给同事,或者贴进团队规范里:

把 Codex 安全跑起来的默认做法是:在本地或 CI 用容器/虚拟机做沙箱,默认禁网并限制可执行命令;仓库用只读 checkout,让它只能通过 PR 输出修改;云密钥用短期令牌与最小权限,运行时注入且禁止写入磁盘;对“联网、推送、部署、删改资源”等高风险动作强制人工审批,并保留完整审计日志与一键回滚路径。


先把“危险动作”说清楚:你到底怕什么?

我见过最常见的翻车,不是它写错代码,是它做了你没想到它会做的“顺手动作”。

危险动作与安全开关映射图 你让它“升级依赖”,它可能顺便:

  • 改了 lockfile 的生成方式,导致构建缓存失效(账单飙)
  • .env、CI 变量、调试输出打进日志(密钥泄露)
  • 看到脚本里有 deploy,就尝试跑一遍(生产被碰瓷)
  • 为了“修复测试”,改了测试本身(你还以为稳定了)

所以安全策略别写成“谨慎使用”。要把风险拆成能落地的开关:文件、网络、进程、权限、密钥、审计。

有次在 Discord 里,读者问过我一句特别实在的话:“它都已经能跑测试、改代码了,为什么不能顺便部署?”我当时第一反应是,理论上可以。可把这事往下拆,你就会发现“顺便部署”这四个字里,藏着网络出口、环境变量、制品来源、回滚路径和审批责任,根本不是一个动作,而是一串动作。

也正因为这样,我现在越来越倾向于把代理看成流水线里的一个工位,而不是一个可以无限外扩的执行者。它做得越多,你越要把边界切得更细。这里面有没有更优雅的自动化方案?应该有,但我自己目前更信工程上的笨办法。


三道闸:照着 OpenAI 的思路抄一套默认值

这一段是整篇的核心观点:别纠结“给不给代理权限”,要纠结“权限怎么分三次给”

代理权限三道闸默认策略图

第一道闸:沙箱隔离(文件 / 网络 / 进程)

把代理想成在厨房里学做菜的新厨子:刀给它、锅给它,但先别给燃气总阀,也别让它随便出门买东西。

你要的隔离,至少覆盖三件事:

  1. 文件隔离
  • 只挂载工作目录(项目代码),别把整块 Home 目录塞进去
  • 禁止访问 SSH 目录、浏览器配置、云 CLI 默认凭证目录(这些地方全是“现成钥匙”)
  1. 网络隔离
  • 默认禁网,尤其是本地跑的时候
  • 要放网也要“只放允许域名”,比如只允许访问你公司的包仓库、镜像源
  1. 进程隔离
  • 限制它能起的进程类型(只允许 gitnodepytest 这类)
  • 禁止它直接调用系统层面的破坏性命令(后面白名单会讲)
⚠️ 别把“沙箱”理解成 Docker 就够了:Docker 默认仍可能读到宿主机的环境变量或凭证路径,也可能被你一条不小心的挂载把密钥带进去。隔离强度取决于你“挂进去什么”和“放出去什么”。

第二道闸:分级权限 + 人工审批(把高风险动作变成“需要签字”)

“让它自己跑完”听起来很爽,但真正稳的是:低风险动作自动化,高风险动作走审批

我建议你用这张表定团队默认值。你一个人做项目,其实也一样适用:

动作类型默认策略你要的反馈(做对了长啥样)
读代码、跑测试、静态分析自动允许产出可复现的命令与日志,CI 能重跑
修改代码(工作分支)允许,但只能 PRPR 里有 diff,能逐行 review
推送到 main / 改 tag / 发 release必须人工确认触发前弹出确认点,且记录操作者
联网下载依赖默认允许“固定来源”,否则确认日志里能看见访问域名与版本号
执行 rm / 清库 / 改权限 / 写系统目录一律阻断明确报错:需要人工接管
部署到云、迁移数据库必须人工确认 + 双人复核(可选)有审批记录 + 可回滚脚本

这里的关键不是“你不信任它”,而是你把责任边界画出来了:代理负责产出,主人负责批准。


第三道闸:密钥最小暴露(短期、最小权限、只在运行时出现)

真正让人睡不着的,是密钥。

你想要的不是“把密钥藏好”,而是做到三件事:

  • 短期:用临时令牌,能 30 分钟到几小时过期的那种,别用长期 Access Key
  • 最小权限:只给它完成任务需要的那一小撮权限,比如只能写某个 bucket、只能部署到 staging
  • 不落盘:密钥只在运行时通过环境变量注入,禁止写入 repo、禁止写入日志、禁止写入产物
💡 实用心法:你要把“密钥”当成一次性手套。用完就丢;丢了也别心疼,因为它本来就该随时能换。

方法论:一套能照抄的“默认禁用 → 逐步放行”流程

下面是落地部分。按这个顺序做,最不容易漏。

默认禁用→逐步放行五步流程图

Step 0:先定“运行形态”(本地 vs CI)

  • 本地:优先强隔离(容器/虚拟机),默认禁网
  • CI:把代理当作一个 job,权限跟 job 走,所有动作必须有日志、可重跑

你做完这一步,应该能回答一句话:代理跑在哪台机器上?那台机器上有哪些它不该看到的东西?


Step 1:仓库默认只读,改动只能走 PR

目标:让“可追溯”成为默认值。

操作要点(平台无关):

  1. CI checkout 用只读 token,或者直接匿名拉取公开仓库
  2. 禁止代理持有能直接 push main 的凭证
  3. 改动必须以 PR 形式输出,哪怕是你一个人,也当成流程

你做对了的标志是:代理即使想“悄悄改点东西”,也只能留下一个你看得见的 diff。


Step 2:命令白名单(让它只能用“厨房刀具”,别摸电锯)

这里给你一个可复制的白名单模板:把允许的命令分三层——允许、需要确认、禁止。

你可以先从这套起步,再按你项目语言改:

  • 允许(自动执行):git statusgit diffnpm testpytestruffgo testmvn test
  • 需要确认(弹窗/审批):git pushdocker buildterraform plankubectl diff
  • 禁止(直接拒绝):rm -rfchmod -Rchownterraform applykubectl apply、任何直接连生产的命令

为了让你今天就能用,我给一个最小的审批提示词(prompt),你可以塞到任何代理的系统提示或团队规范里:

text你是代码代理。默认只能:读仓库、在隔离环境运行测试/静态检查、在工作分支修改代码并通过 PR 提交建议。
当你准备执行以下动作时,必须先停止并请求人工确认:联网访问非白名单域名、git push、发布/部署、写入仓库以外路径、删除文件、修改权限、运行基础设施变更命令。
如果发现可能包含密钥/凭证的内容(如 .env、API key、token、私钥),必须在输出中打码并提示风险,不得写入文件或日志。

Step 3:密钥注入策略(Mac/Linux 和 Windows 两套)

原则:密钥只给“需要它的那一步”,不给整条流程。

Mac/Linux(运行时注入到单个命令):

bashAWS_ACCESS_KEY_ID=... AWS_SECRET_ACCESS_KEY=... npm run deploy:staging

Windows PowerShell(同样只对当前会话/命令生效):

powershell$env:AWS_ACCESS_KEY_ID="..."; $env:AWS_SECRET_ACCESS_KEY="..."; npm run deploy:staging

你做对了的标志是:

  • repo 里搜不到密钥
  • 构建产物里搜不到密钥
  • 日志里也搜不到密钥,至少不会明文出现

Step 4:审计与回滚(让“出事”变成一次演练,而不是一次事故)

你不可能保证永远不出事,但你可以保证“出事时别瞎”。

最低配你也要有:

  • 审计:保存代理执行过的命令清单、拉取/安装了哪些依赖、生成了哪些文件
  • 回滚:所有改动都在分支/PR;部署要有对应的回滚命令,或上一版本可一键恢复
  • 复现:CI 里能重跑同一套命令,结果一致,至少失败方式一致

我自己的习惯是:只要代理碰过“部署 / 依赖升级 / 配置变更”,我就会要求它在 PR 描述里附一段“回滚说明”。写不出来的回滚,基本都不该自动执行。

说白了,安全配置的价值不只是在“拦住一次坏动作”,还在于它能让你复盘。很多人把事故当成模型问题,我越来越觉得,更多时候是流程没有留下抓手。等你想回看那一步到底是谁批的、用了什么密钥、访问了哪些域名,结果什么都没记,那就不是模型太聪明,是系统太糊涂。

如果你现在手上已经有一条能跑的 agent 流程,不妨先别急着加能力。先做一件小事:挑出其中最危险的那个动作,把它改成“默认不执行,必须确认”。通常改完这一处,你对整套系统的控制感就会完全不一样。


你可以直接照抄的“高风险操作确认清单”

把下面这份当成你每次点“允许”之前的过脑子 checklist:

  1. 这一步会不会写生产?会 → 必须审批
  2. 这一步会不会联网?连哪?不确定 → 必须审批
  3. 这一步会不会读到密钥?会 → 改成短期令牌 + 运行时注入
  4. 这一步失败了能不能回滚?不能 → 先补回滚再执行
  5. 这一步执行完,我能不能解释“它做了什么”?不能 → 先补审计

到这里你会发现:你要的不是“更聪明的代理”,而是一套让你敢用它的安全地板。

FAQ

Q: 我就一个人做项目,也要搞审批吗?
A: 要,但可以简化成“高风险动作必须手动确认”。你一个人更容易手滑,流程不是官僚,是防呆。

Q: 默认禁网会不会太麻烦?
A: 本地默认禁网更稳;真需要联网就改成“域名白名单 + 人工确认”。麻烦一点,换来的是不会把凭证和环境一起暴露。

Q: PR 流程会不会拖慢效率?
A: 早期反而更快:你能一眼看 diff、能回滚、能复盘。真正拖慢的是线上出一次事故后全盘排查。

如果你打算今天就把 Codex 接进项目,我建议别先研究 prompt,先把三件事补上:沙箱、审批点、短期密钥。做完以后,再回头看你的流程里还有哪一步其实默认信得太多了。那一步,往往才是下一次事故真正会长出来的地方。

Source & Credit

灵感来源于 YouTubeOriginal Thread