headless 的安全问题和交互式不一样:没人看着,所以「默认权限」实际上就是「全部权限」。 五条原则:最小权限、密钥不入库、容器隔离、成本防护、审计留痕。 一份最简配置真跑下来只过了两条。
前面几节把流水线搭起来了,这一节补最重要的一课:这些权限开出去,出了事怎么兜。 这一节有一个真跑的检查器:拿一份能跑的最简配置,五条原则逐条查。
一、五条原则各自的落点
最小权限:只给它干活必需的那几项。落在 permissions 段和允许工具列表上。 密钥不入库:密钥只能从环境或密钥库来。落在 secrets 上。 容器隔离:跑在一次性环境里,跑完就丢。落在运行环境的选择上。
成本防护:有上限,超了就停。落在轮数上限和超时上。 审计留痕:每次跑了什么、改了什么都留档。落在日志和产物上。 五条都能落到具体的配置项上——落不到配置项的原则,等于没写。
二、真跑:一份配置过了几条
演示拿一份「能跑」的最简配置逐条查:过了两条,没过三条。 过的两条是容器隔离和审计留痕——因为工作流本来就跑在托管环境上,本来就有输出文件。 没过的是最小权限、密钥、成本防护——这三条恰好都是「不写就沿用默认值」的项。
「密钥不入库」这条没过的原因值得看一眼:它没把密钥写进文件,而是 echo 到了日志里。 一样是泄露,只是换了个地方。而日志通常比文件留得更久、看的人更多。
三、每一条缺了会怎样
缺最小权限:令牌能改代码、能删分支。一次误操作直接进仓库。 缺密钥管理:密钥被打进日志。日志谁都能看,而且留得久。 缺容器隔离:跑在常驻机器上。上一次的残留会影响这一次。
缺成本防护:没有上限,跑不完还一直跑,账单出来才知道。 缺审计:只剩一行退出码。出事回溯时什么都看不到。 五条里有三条的后果很难撤回:进了日志、进了仓库、账单已经产生。
四、两条能立刻做的改动
第一条:删掉那行 echo。调试用的打印留在配置里,是最常见的泄露来源。 第二条:把默认权限从「能写」改成「只读」,需要写的地方单独开。 两条加起来不超过五行,但它们挡掉的是一整类事故。
这一条和 6.8 节那句是同一个意思:安全不能靠自觉,要靠「做不到」。
五、为什么 headless 的安全更难过
交互式有人看着:它要做危险动作,你能看见、能打断、能事后撤销。 无人值守这三道都没有。所以只能靠「一开始就不给它那个权限」。 还有一条更微妙的:headless 的输出会进日志、进评论、进别的地方。
所以 「泄露」的范围比本地大得多——本地跑错只脏了你的屏幕,CI 里跑错留的是永久记录。
六、动手 Demo:五条安全原则,一份配置过了几条
headless 的安全问题和交互式不一样:没人看着,所以「默认权限」就是「全部权限」。 五条原则写成五条检查,拿一份能跑的最简配置逐条查。
checks = [
(u"最小权限", lambda t: u"permissions:" in t),
(u"密钥不入库", lambda t: re.search(r"echo[^\n]*(API_KEY|TOKEN|SECRET)", t) is None),
(u"成本防护", lambda t: u"--max-turns" in t or u"timeout-minutes" in t),
]
实跑输出:
--------------------------------------------------------------------
五条安全原则,一份配置过了几条
--------------------------------------------------------------------
headless 的安全问题和交互式不一样:没人看着,所以「默认权限」就是「全部权限」。
五条原则:最小权限、密钥不入库、容器隔离、成本防护、审计留痕。
① 五条原则各自的落点
--------------------------------------------------------------------
| 原则 | 要求 | 落在哪 |
|---|---|---|
| 最小权限 | 只给它干活必需的那几项 | permissions 段、--allowed-tools |
| 密钥不入库 | 密钥只能从环境或密钥库来 | secrets、credentials() |
| 容器隔离 | 跑在一次性环境里,跑完就丢 | runs-on、docker run --rm |
| 成本防护 | 有上限,超了就停 | --max-turns、timeout-minutes |
| 审计留痕 | 每次跑了什么、改了什么都留档 | 日志上传、产物保存 |
② 真跑:拿一份最简配置逐条查
--------------------------------------------------------------------
| 原则 | 结果 | 配置里 |
|---|:---:|:---:|
| 最小权限 | 没过 | 缺 |
| 密钥不入库 | 没过 | 缺 |
| 容器隔离 | 过 | 有 |
| 成本防护 | 没过 | 缺 |
| 审计留痕 | 过 | 有 |
五条只过了 2 条。这份配置能跑,但它把权限、成本和痕迹全留成了默认值。
「密钥不入库」这条没过,原因值得看一眼:它没把密钥写进文件,而是 echo 到了日志里。
一样是泄露,只是换了个地方——而日志通常比文件留得更久、看的人更多。
③ 每一条缺了会怎样
--------------------------------------------------------------------
| 缺哪条 | 直接后果 | 最坏情况 |
|---|---|---|
| 缺最小权限 | 令牌能改代码、能删分支 | 一次误操作直接进仓库 |
| 缺密钥管理 | 密钥被打进日志 | 日志谁都能看,而且留得久 |
| 缺容器隔离 | 跑在常驻机器上 | 上一次的残留会影响这一次 |
| 缺成本防护 | 没有上限 | 跑不完还一直跑,账单才知道 |
| 缺审计 | 只有一行退出码 | 出事回溯时什么都看不到 |
五条里有三条的后果是很难撤回的:进了日志、进了仓库、账单已经产生。
所以安全这件事在 headless 里不是优化项,是准入条件。
④ 两条能立刻做的改动
--------------------------------------------------------------------
第一条:删掉那行 echo。调试用的打印留在配置里,是最常见的泄露来源。
第二条:把默认权限从「能写」改成「只读」,需要写的地方单独开。
两条加起来不超过五行,但它们挡掉的是一整类事故。
! 检查器是文本扫描,只认关键字,不是真的语义检查——它的作用是提醒,不是把关。
①五条原则都能落到具体配置项上:最小权限落 permissions、密钥落 secrets、容器隔离落运行环境、 成本防护落轮数上限和超时、审计留痕落日志和产物。 落不到配置项的原则,等于没写。 ②一份能跑的最简配置只过了两条:容器隔离和审计留痕(因为本来就跑在托管环境上、本来就有输出文件)。 没过的是最小权限、密钥、成本防护——这三条恰好都是「不写就沿用默认值」的项。 ③「密钥不入库」没过,原因值得看:它没把密钥写进文件,而是 echo 到了日志里。 一样是泄露,只是换了个地方,而日志留得更久、看的人更多。 ④五条里三条的后果很难撤回:进了日志、进了仓库、账单已经产生。所以安全不是优化项,是准入条件。
30 秒实验
- 把 echo 那行删掉、加上 permissions 段,重跑一遍。过了几条?还剩哪一条没过? 它为什么必须靠「跑一次看账单」才能发现?
- 给检查器加第六条「容器跑完即销毁」,重跑一遍。这一条能用文本扫描查出来吗?查不出来说明什么?
- 把工作流改成「先在本地跑,再把结果贴到 PR」,重跑一遍。审计留痕这一条还成立吗?
常见误区
- 不写 permissions,用平台默认权限。默认往往比你要的大。
- 把密钥 echo 到日志里调试。这和写进文件一样是泄露,而且日志留得更久。
- 给「审查」任务开写代码的权限。它能自己改,审查结论就不独立了。
- 不设成本上限。跑不完还一直跑,账单出来才知道。
- 以为容器隔离是「部署的事」。它决定了上一次的残留会不会影响这一次。
自测三问(点开对答案)
1. 五条安全原则是什么? —— 最小权限、密钥不入库、容器隔离、成本防护、审计留痕。 2. 为什么「密钥 echo 到日志」和「写进文件」一样严重? —— 因为都是把密钥暴露到了不该出现的地方。日志通常留得更久、看的人更多。 3. headless 的安全为什么比交互式更难过? —— 交互式有人看着,能看见、能打断、能撤销;无人值守这三道都没有,只能一开始就不给那个权限。安全兜住了,下一节往上走一层:不用命令行,直接在程序里调。
- ☐ (本篇完,下一篇 7.11 从 CLI 到 Agent SDK:Headless 模式的编程接口)
相关笔记
- 安全机制:工具侧收窄那三条规矩
- 从 CLI 到 Agent SDK:从 CLI 到 SDK
- Claude Code 橙皮书 · 索引
- 🐎 Claude Code 实战 · 索引