# 为编码智能体隔离预发布环境和生产环境的访问权限

编码智能体绝不能通过更换一个变量、一个 URL 或一条含糊的指令，在预发布环境和生产环境之间切换。独立凭据和独立目标可以降低误调用另一环境的可能性。在生产边界设置人工决策，则能让有意执行的操作具备明确责任归属。

我见过一些团队因为代码仓库里有两个主机名，就把自己的配置称为“独立环境”。后来，部署脚本继承了生产令牌，测试夹具指向了线上项目，或者智能体在 Shell 会话中找到了凭据，并使用了碰巧可用的路径。主机名是预发布环境，权限却属于生产环境。这不叫隔离。

有一条简单实用的规则：预发布访问应让智能体证明变更有效，同时不具备任何可能影响生产环境的权限。生产访问则应作为独立且范围狭窄的能力存在，由人针对某次具体运行授予，并且能够在运行期间撤回。

## 环境名称不会自动形成安全边界

“预发布”这个标签本身保护不了任何东西。只有当指向预发布环境的调用无法向生产环境完成身份验证，并且到达生产环境的调用在没有单独授予生产身份时无法执行有意义的操作，安全边界才真正存在。

团队经常把三件不同的事情混在一起：

- **路由**决定请求发往哪里，例如 `https://api.staging.example`。
- **身份验证**决定服务看到的是哪个主体，例如服务账户或 SSH 密钥。
- **授权**决定该主体完成身份验证后可以执行什么操作。

只改变路由，另外两项仍然不变。只更换凭据也会留下危险可能：如果账户管理员在两个环境中复用了客户端、角色或密钥，预发布凭据可能同样能够向生产环境完成身份验证。清晰的环境边界必须同时改变这三项。

如果底层服务支持，请从独立的提供商账户、项目、租户、命名空间或订阅开始。它们能提供一个容易核对的硬标识符。独立的数据库、对象存储、队列和部署目标也会自然随之建立。如果服务提供商强制要求预发布和生产共用一个账户，就创建不同的主体和资源范围，并把这个共享账户视为需要额外审查的已知弱点。

不要使用生产客户数据来让预发布环境看起来更真实。未经明确脱敏就复制生产数据，会把问题从访问控制变成隐私暴露。普通开发应使用合成数据。如果测试确实需要类似生产的数据，就生成经过最小化和清洗的数据集，并记录批准其使用的人。

一个实用的边界应当有审核者可以检查的证据：

| 边界元素 | 预发布环境 | 生产环境 |
| --- | --- | --- |
| 目标 | 专用预发布主机名或账户 | 专用生产主机名或账户 |
| 凭据 | 仅限预发布环境的主体 | 仅限生产环境的主体 |
| 权限 | 测试操作和测试资源 | 仅限范围狭窄的线上操作 |
| 数据 | 合成数据或脱敏数据 | 只有任务需要时才使用线上数据 |
| 审批 | 常规开发控制 | 有意为之的人工授权 |

环境与授权域之间的区别很重要。你可以在一个授权域中运行多个环境，但持有共享广泛凭据的智能体可以在它们之间穿行。这种安排有助于运维，却无法提供隔离。

## 生产凭据必须对应不同的身份

生产访问需要独立的主体，而不是给预发布凭据贴上第二个标签。如果同一个 API 令牌、云角色、数据库用户或 SSH 密钥可以在两个环境中操作，那么一次路由错误就可能变成安全事件。

围绕智能体必须执行的操作创建身份。需要检查部署的编码智能体，不应继承修改身份设置、删除存储、读取所有数据库表或在每台主机上打开交互式 Shell 的权限。“智能体可能需要，所以给管理员权限”是一条捷径，会把陌生代码路径变成生产权限。

应按操作后果而不是职位划分访问权限。常见的划分包括：

- 只能写入预发布资源的预发布部署身份。
- 只能读取少量健康状态或发布信息的生产观察身份。
- 可以更新一个服务或发布渠道的生产部署身份。
- 供人类使用、脱离日常智能体工作流的独立紧急身份。

不要给智能体人类开发者的个人令牌。个人令牌会随着时间积累权限，在角色变化后仍然有效，而且通常能访问比持有者记得更多的系统。它们也会让审计记录含义不清：日志显示的是某个人，但请求实际由自主进程发出。

如果服务提供商支持工作负载身份、短期凭据或范围受限的服务账户，就使用它们。短时限有帮助，但无法弥补过大的权限范围。十分钟有效的管理员凭据，仍然可以在第一分钟删除生产数据库。

NIST Special Publication 800-53 在控制项 AC-6 中描述了最小权限原则：组织应只授予完成指定任务所必需的访问权限。这听起来很明显，但当智能体需要快速修复时，人们往往会直接选择所有者角色。标准不会替你决定具体权限，却会迫使你提出正确的问题：移除这项权限后，究竟哪一个单独操作会失败？

对于 SSH，独立密钥是必要条件，但还不够。让生产密钥只能访问指定主机或受限账户；在环境允许时禁用宽泛的转发路径；不要把本应用于单个部署命令的凭据放在通用 Shell 后面。能够打开不受限制的生产 Shell 的密钥，会给智能体带来庞大且难以审核的操作面。

## 端点隔离需要可执行的检查

配置应让跨环境混用在 API 请求离开机器前就失败。不要要求智能体记住自己当前处于哪个环境。让所选配置同时携带目标和身份名称，并拒绝那些永远不应允许的组合。

下面的 Shell 模式不会保存密钥。它会在包装器或凭据代理执行请求前，验证用于选择密钥和端点的值：

```sh
#!/usr/bin/env sh
set -eu

case "${AGENT_ENV:?set AGENT_ENV}" in
  staging)
    API_BASE="https://api.staging.example.internal"
    CREDENTIAL_REF="agent-staging-deploy"
    ;;
  production)
    API_BASE="https://api.example.com"
    CREDENTIAL_REF="agent-production-deploy"
    ;;
  *)
    printf '%s\n' "AGENT_ENV must be staging or production" \u003e\u00262
    exit 64
    ;;
esac

printf 'environment=%s\nendpoint=%s\ncredential_ref=%s\n' \
  "$AGENT_ENV" "$API_BASE" "$CREDENTIAL_REF"
```

预发布调用会产生类似这样的输出：

```text
environment=staging
endpoint=https://api.staging.example.internal
credential_ref=agent-staging-deploy
```

把这段输出当作预检记录，而不是授权证明。脚本只能阻止本地的错误组合。凭据存储或操作网关仍必须拒绝解析 `agent-production-deploy`，除非人类已经授予生产访问权限。

避免接受智能体任意 URL 的配置。类似 `curl "$TARGET"` 的请求接口搭配 bearer 令牌，会把每个生成的字符串都变成潜在目标。即使是谨慎的智能体也会犯错，而代码仓库中的不可信文本可能影响它的工具调用。应改为提供命名操作或固定端点配置。

同样的原则适用于 SSH。不要在生产密钥旁暴露通用的 `host` 参数。将生产部署操作绑定到预期的主机组和远程命令，或者要求人类在审批时选择目标。

只比较 `production` 这样的字符串，会留下一个弱点：名称可能撒谎。还要检查提供商标识符。云账户号码、项目 ID、订阅 ID、代码仓库所有者或数据库集群 ID 都更不容易因为有人复制配置文件而发生漂移。

## 提示词无法授权线上变更

“绝不要接触生产环境”这样的指令可以提供有用背景，但它不是访问控制。智能体会遵循工具输出、代码仓库文件、任务描述和自己的中间计划，其中任何一项都可能造成冲突或混淆。提示词不会位于网络请求之前，替你拒绝请求。

生产决策需要一个位于智能体进程之外的强制执行点。这个点应知道哪个进程请求了操作、想使用哪个生产身份、调用将发往哪里，以及具体操作是什么。然后它应要求明确的人工选择，或者拒绝调用。

按会话审批和按调用审批解决的是不同问题。

当人类已经审核过范围明确的运行时，按会话审批很合适，例如智能体将经过审核的部署计划应用到一个服务。它可以减少重复打断，同时把权限绑定到特定进程。审批应在该进程退出时过期，而不是因为终端仍然打开就持续到当天结束。

对于不可逆或影响较大的操作，按调用审批更合适，例如删除数据、轮换凭据、改变网络暴露、发布版本或写入生产数据库。要求每次使用都点击或进行本地身份验证，本来就是有意放慢速度。这种摩擦是在提醒你，该操作值得关注。

不要训练人们去审批看不懂的卡片。审批界面应清楚显示进程身份、目标、凭据身份、方法和请求摘要，让人能够作出判断。“智能体请求访问”没有提供任何可供审核的信息。“已签名的编码进程请求使用生产部署身份向生产部署端点发送 POST 请求”则足以发现不匹配。

Sallyport 直接采用了这种划分：本地身份验证打开保险库前，保险库始终保持锁定；随后，新智能体进程默认需要会话授权，而单个凭据还可以设置为每次使用都需要审批。智能体永远不会收到 API 或 SSH 密钥本身。

这种设计拒绝了一条流行建议：把生产令牌放进严格控制的环境变量，并依赖谨慎的提示词。这种方式流行，是因为很容易接入现有脚本。但它会失败，因为智能体进程仍然持有权限，任何能够读取其环境或复用其进程的工具调用都可能使用该权限。

## 失败通常始于一个看似无害的便利

跨环境事件很少始于某人决定攻击生产环境。它们通常始于一个小便利，而这个便利移除了某项检查。

想象一个包含两个配置的发布代码仓库。团队在测试运行时使用 `DEPLOY_ENV=staging`，在线上运行时使用 `DEPLOY_ENV=production`。由于早期测试方便，两个配置都从同一个开发者 Shell 中读取 `DEPLOY_TOKEN`。这个令牌可以访问两个部署项目。

智能体收到任务，要验证一次预发布发布。它读取了一个根据 `DEPLOY_ENV` 构造端点的脚本。同一个终端中的上一条命令留下了 `DEPLOY_ENV=production`，而后面的辅助工具又根据独立配置文件打印出预发布标签。智能体看到这个标签，运行辅助工具，最终请求发往生产端点，并使用了在那里同样有效的令牌。

整个过程不需要恶意行为，也不需要罕见漏洞。团队有两个标签、一个共享凭据、两个事实来源，却没有审批点。日志甚至可能显示普通开发者账户，因为共享令牌属于该开发者。

更谨慎地设置 `DEPLOY_ENV=staging`，只能修复表面问题，无法修复设计。真正的修复是结构性的：

1. 用环境专属身份替代共享令牌。
2. 将每个身份绑定到允许使用的账户、项目或资源集合。
3. 通过代理或保险库解析身份，而不是从智能体的 Shell 环境中读取。
4. 在生产身份可以发出调用前，要求人工授权。
5. 记录进程、目标、身份引用、操作、结果和审批决定。

如果可能，将生产和预发布设置放在一份经过审核的事实来源中，但不要让它们可以互换。只复制配置块并修改主机名，正是隐蔽漂移的开始。每个配置都应明确到足以让审核者并排比较账户 ID、凭据引用和允许的操作。

## 生产审批应描述一次范围明确的运行

只有当人类能够把审批与一项明确的工作联系起来时，人工审批才有价值。“允许这个智能体访问生产环境”范围太大。它授予了一项没有明确终点、审核者也无法识别的能力。

使用具体边界定义一次生产运行：智能体进程、代码仓库或任务、目标服务、允许的操作类别以及过期条件。具体机制取决于你的工具，但第一次调用前，决策应回答以下问题：

- 哪个本地进程正在请求，能否识别它的代码签名者或可执行文件？
- 哪个生产账户或端点会接收请求？
- 操作将使用哪个凭据身份？
- 智能体在这次运行期间可以执行什么操作？
- 授权何时结束，谁现在可以撤销它？

进程身份值得更多关注。在终端中显示的名称或智能体自报名称都很容易伪造。在受管控的开发者机器上，代码签名者身份能更有力地说明是哪个程序发起了请求。它不能证明任务本身合理，但有助于阻止另一个本地进程冒用熟悉的名称。

审批疲劳是设计失败。如果每个无害的预发布请求都要求点击，人们就会不看内容直接批准。通过独立的狭窄凭据，让正常的预发布工作保持顺畅。只有目标和后果值得打断时，才为生产调用显示提示。

反过来的错误更糟：一次审批默默覆盖未来所有智能体进程。几天后，这与永久生产访问没有区别。将审批绑定到本次运行，在运行结束时使其过期，并让撤销立即生效，而不是变成等待他人处理的工单。

对于会改变多个生产资源的部署，不要假装五次独立审批会让审核更好。只有在计划本身范围明确且可见时，才请求一次会话授权。如果每次调用都可能产生不同的不可逆影响，就要求逐次审批。控制措施应匹配操作，而不是为了完成仪式。

## 审计记录需要回答是谁使用了权限

生产审计日志应让你无需相信智能体自己的叙述，就能重建一次操作。聊天记录或终端历史可以帮助调查，但它们可能不完整、被编辑，或与实际发出调用的凭据脱节。

在凭据实际使用的强制执行点记录请求路径。记录智能体进程身份、会话标识符、请求的凭据引用、解析后的目标、方法或 SSH 命令类别、时间戳、审批结果、响应状态以及撤销事件。对密钥和敏感请求正文进行脱敏；有用的审计记录不应变成客户数据泄露的新来源。

NIST SP 800-53 控制项 AU-2 要求定义组织记录的事件。这一点很重要。“我们记录智能体活动”并不是定义。应决定拒绝的请求、审批、凭据使用、目标不匹配和会话撤销是否都算事件。如果看不到被拒绝的请求，就无法判断边界阻止了一次错误，还是请求根本没有到达。

事件发生后，防篡改证据很重要，因为普通应用日志通常保存在管理员或被入侵进程可以修改的位置。哈希链日志可以在你用存储的记录验证链条时发现修改。它不能把错误决定变成正确决定，也不能替代备份、访问审查或外部留存，但能帮助调查人员发现记录是否被改动。

Sallyport 从一份加密、哈希链式审计日志生成 Sessions 和 Activity 视图，`sp audit verify` 可以在没有保险库密钥的情况下离线检查链条。当你需要确认记录是否发生变化，而不是相信智能体声称自己做了什么时，这很有用。

建立与风险相匹配的审查习惯。在重要的智能体运行后审查生产授权和被拒绝的尝试。定期将正在使用的生产身份与它们实际执行的操作进行比较。日志中从未出现过的权限，应该考虑移除。无法解释的权限，范围已经过大。

## 轮换和撤销必须能在事件期间生效

只有在能够迅速停止使用生产凭据时，独立凭据才能限制损害。如果轮换计划要求找到每个脚本、编辑每台工作站，并等待每周部署窗口，那就不是事件控制措施。

为每个生产智能体身份指定负责人、签发位置、允许的目标列表和明确的撤销路径。将这些信息与密钥值分开保存。事件期间，响应人员需要知道该禁用什么，而不应为了寻找答案打开可能包含凭据的文件。

在真正需要前，先测试以下流程：

1. 启动一次获批的生产智能体运行，让它执行无害且可逆的操作。
2. 撤销会话或禁用其生产凭据。
3. 让智能体重复该操作。
4. 确认强制执行点拒绝请求，并且拒绝记录出现在审计日志中。
5. 签发替换凭据，并确认预发布访问仍能独立工作。

这项测试能发现常见的运维缺陷：界面显示“已撤销”，但缓存令牌、持久化 SSH 连接或长期运行的进程仍然有效。对于 API，检查令牌有效期和刷新行为。对于 SSH，检查现有连接和多路复用。只有下一次尝试确实失败，撤销按钮才值得信赖。

分别轮换预发布和生产身份。如果预发布密钥泄露，你不应因此中断生产。如果生产权限的安全性变得不确定，即使疑似暴露看起来很小，也应撤销并替换它。人们经常低估令牌通过 Shell 历史、调试输出、复制的日志或智能体工具上下文传播了多远。

## 在授予线上访问前先建立边界

团队应通过证据获得生产访问，而不是凭借对提示词或智能体模型的信心。编码任务通常有预发布路径、试运行、只读查询或人工发布流程，可以先证明大部分工作。

在允许新的生产操作前，使用以下准备度检查：

- 预发布和生产使用不同凭据，并且彼此无法完成身份验证。
- 智能体无法从文件、环境变量、提示词或工具输出中读取明文密钥。
- 请求路径会检查固定目标，以及提供商账户或项目标识符。
- 人类能看到生产操作，并在智能体进程之外授予权限。
- 撤销、拒绝日志记录和审计验证都已经实际测试，而不是仅仅假定可用。

如果有一项不满足，就让操作留在预发布环境，或由人类直接执行生产工作。这不是智能体自动化的失败，而是准确说明控制路径尚未完成。

最适合作为早期生产权限的，通常是针对非敏感状态端点的狭窄观察操作。它可以在不允许智能体改变面向客户的状态的情况下，测试目标选择、身份隔离、审批、日志记录和撤销。等这条路径经受真实使用后，再一次增加一个写操作，并移除智能体从未需要的权限。

不要因为审批带来不便，就在之后合并预发布和生产访问。生产边界上的摩擦，是确认谁授权了线上操作、哪个进程执行了它，以及如何停止它所付出的代价。让这条边界始终保持明确。
