# AI 智能体的 HTTP 身份验证：安全的凭据模式

AI 智能体应该能够请求执行 HTTP 操作，却始终不持有让该操作成为可能的凭据。这个原则比请求使用 bearer token、Basic 身份验证还是供应商标头更重要。如果密钥进入智能体上下文，就可能通过提示词、工具追踪、生成的 Shell 命令、代码库文件，或没人打算保留的后续摘要泄露出去。

HTTP 身份验证模式仍然重要，因为每种模式都会影响凭据可能被窃取、重放、意外转发和审计的方式。正确的设计应从 API 要求的方案开始，然后把凭据限制在受信任的执行器中，由它代表智能体发送范围严格定义的请求。

## 智能体会把普通凭据变成可复制的数据

无人值守的智能体会改变普通 API 凭据的风险，因为它会读写大量不同形式的文本。开发者可能把令牌保存在本地凭据存储中，并将它粘贴到一次请求里。智能体则可能检查环境变量、写入调试输出、组合 curl 命令、创建配置文件，再把工作结果报告给人类。每个操作都会增加一个可重复使用的密钥落地位置。

危险路径一开始往往看起来很普通：

1. 任务运行器把 `PAYMENTS_TOKEN` 放进智能体进程的环境变量。
2. 智能体运行诊断命令，打印环境变量或写入 Shell 脚本。
3. 脚本进入代码库、CI 构件、终端滚屏记录，或另一次智能体工具调用。
4. 有人稍后找到这个令牌，并在它过期或操作员撤销之前持续发送有效请求。

这不需要复杂的攻击者。只要令牌变成了某个专门用于复制文本的地方中的文本，就足够了。

不要把针对智能体的访问控制和凭据保密混为一谈。沙箱可以阻止智能体打开目录之外的文件，但如果密钥已经出现在模型上下文、命令参数或工具结果中，沙箱就无能为力。同样，要求用户确认智能体是否可以运行 `curl` 的审批提示也说明不了多少，如果智能体仍能自由提供主机、路径、请求体和继承来的授权标头。

因此，AI 智能体的 HTTP 身份验证需要两个独立的边界。智能体需要获得提出请求的权限。受信任的组件则需要保管凭据，并拥有发送请求的权限。把两个边界合在一起，就会让智能体拿到可复制的密钥，并让后续控制依赖智能体始终完美地处理它。这种设计假设不合理。

一个简单的测试方法是：问问自己，如果不撤销凭据，能否把完整的智能体对话记录粘贴到工单系统中。如果答案是否定的，说明密钥已经越过了错误的边界。

## Bearer token 易于发送，也容易被重放

Bearer token 会向任何出示它的人授予访问权限。因此，除非你接受“对话记录泄露就可能变成 API 访问泄露”这一结果，否则绝不能把它交给智能体。RFC 6750 规定了在 `Authorization` 请求标头中使用 bearer token 的方式：

```http
GET /v1/projects/alpha/releases HTTP/1.1
Host: api.example.test
Authorization: Bearer eyJhbGciOi...
Accept: application/json
```

服务器不需要证明发送者是指定的智能体、原始用户或原始机器。它只需检查提供的令牌是否有效并获得授权。这个特性让 HTTP 客户端保持简单，但也意味着任何拿到复制令牌的人都能使用它。

RFC 6750 允许在有限条件下通过表单请求体发送 bearer token，也描述了在旧式场景中通过 URI 查询发送的方式。不要把它放进查询字符串。URL 比大多数团队预想的更容易进入浏览器历史记录、代理日志、分析系统、Referer 标头、支持工单和应用日志。该标准本身也警告说，通过 URI 传输很容易泄露。没有理由让智能体的密钥成为 URL 的一部分。

只有在周边凭据设计能够限制损害时，bearer token 才适合智能体。优先选择具有明确 API 受众、权限范围窄、有效期短，并且为操作执行器设置独立身份的令牌。一个可以管理所有项目、读取所有客户记录且永不过期的 API token，本质上就是生产环境的主凭据，只是名字听起来更友好。

把 bearer token 交给智能体的常见理由是速度：一个环境变量、一个 HTTP 客户端，不需要额外组件。它在演示中很受欢迎，因为确实能用。但一旦智能体需要调试、委托、长时间运行，或访问多个服务，这种做法就会失败。被复制进上下文的令牌更难有选择地撤销，因为你已经不知道它去过哪些地方。

还有一个陷阱：名字看起来权限受限的令牌，实际权限仍可能很广。应阅读供应商文档，了解真实的权限范围模型。有些服务按端点设置权限范围，有些按组织、项目、代码库或账户授予权限。有些 API token 会悄悄继承创建者用户的全部权限。令牌标签不能证明它的限制。

中介可以保管 bearer token，并在验证请求目标后才构造标头。智能体应提交意图和请求数据，例如“使用这个请求体在 alpha 项目中创建一个发布”，而不是提交字面形式的 `Authorization` 标头。执行器在验证后添加密钥，并在向智能体返回任何记录前将其移除。

## Basic 身份验证需要独立的服务身份

通过 TLS 使用受限的服务账户时，Basic 身份验证可以接受，但用人类用户的用户名和密码来授权智能体是很差的做法。RFC 7617 规定的线路格式，是将 `user-id:password` 进行 Base64 编码后放入 Authorization 标头：

```http
Authorization: Basic YWdlbnQtcmVsZWFzZXI6czNjcjN0LXZhbHVl
```

任何能读取该值的人都可以将它解码。Base64 只是改变了表示形式，并没有保护该值。TLS 可以保护客户端与服务器之间的连接，却无法保护已经被智能体、本地进程、调试日志或代理复制的凭据。

许多 API 供应商会在 Basic 身份验证中使用 API token 作为密码，并使用固定用户名或忽略用户名。这并不会把该方案变成更弱的 bearer 身份验证，而是带来类似的重放风险，以及一些实际问题。客户端或日志记录器可能记录解码后的用户名、原始标头，或两者都记录。开发者也可能因为协议把第二个字段称为密码，就复用真实账户密码。这正是不能委托给自主进程的凭据。

如果无法避免 Basic 身份验证，就为执行器创建专用账户。只授予请求类型所需的权限。不要使用个人账户、管理员账户，或在无关自动化任务之间共享的凭据。独立的服务身份可以支持撤销和调查，不会锁定某个人，也不会一次性破坏所有任务。

要有意识地处理字符编码。RFC 7617 描述了用户名和密码字符集方面的兼容性问题，并允许服务器声明支持 UTF-8。如果供应商只支持普通 ASCII 值，就让机器凭据保持在该字符集内。不要在智能体提示词中自行设计编码步骤，因为这会增加一个可能转换和记录密钥的不一致位置。

请求执行器应该自行从受保护的字段构造 Basic 标头。智能体可以选择已批准的操作并提供非敏感参数，但不应构造 Base64 值，也绝不能在错误消息中看到解码后的凭据。安全的错误消息可以说所选凭据引用的身份验证失败，但不应回显标头，也不应告诉智能体密码的哪一部分匹配。

## 自定义标头需要准确遵循供应商语义

自定义身份验证标头能否安全工作，取决于 API 文档规定的验证规则，以及你对标头值的处理方式。常见例子包括 `X-API-Key`、`Api-Key` 或供应商专用标头。有些供应商要求静态 API key，有些则要求携带时间戳、随机数、规范化路径和请求体摘要的签名请求。把所有自定义标头当成同一种东西，既会导致身份验证失败，也可能让凭据被意外发送到更广泛的范围。

首先，严格遵循供应商规范。HTTP 标头名称不区分大小写，但标头值和签名输入可能区分大小写。签名方案可能要求特定的规范化顺序、完全一致的请求体字节以及有限的时间戳窗口。如果执行器先解析 JSON，再重新序列化后签名，就可能生成看起来有效但字节序列不同的 JSON。供应商随后会拒绝请求，人们往往因此关闭签名检查，或加入宽泛的重试逻辑。正确的做法是修复字节处理。

其次，要区分用于身份验证的标头和用于标识客户端的标头。`User-Agent`、请求 ID 和应用标识可以帮助供应商观察流量，但通常不能证明权限。反过来，`X-API-Key` 标头可能和 `Authorization: Bearer` 一样容易被重放。不要因为标头名称中没有“authorization”这个词，就低估它的敏感性。

第三，要阻止智能体进行标头注入。如果受保护的执行器还会注入凭据，就不要给智能体一个可以自由填写的出站标头映射。自由填写的映射允许它添加第二个 `Authorization` 标头、覆盖预期的内容类型、附加未经批准的身份标识标头，或以审查者看不到的方式影响下游代理。

使用包含命名字段和类型约束的请求契约。例如：

```json
{
  "credential_ref": "release-service",
  "method": "POST",
  "url": "https://api.example.test/v1/projects/alpha/releases",
  "headers": {
    "accept": "application/json"
  },
  "body": {
    "version": "2025.06.0",
    "notes": "Fix parser crash on empty input"
  }
}
```

由执行器而不是智能体负责将 `credential_ref` 映射到供应商的自定义标头或签名流程。它应该拒绝智能体提供 `authorization`、`cookie`、供应商凭据标头、`host`，以及这些名称的重复形式。`Content-Length` 也应由执行器负责，因为 HTTP 客户端必须根据最终字节数计算它。

向智能体展示响应时也要遵守同样的原则。HTTP 响应可能包含 `Set-Cookie`、诊断信息或回显的请求细节。只返回任务所需的状态、经过选择的安全响应标头和请求体。如果操作员需要原始标头和追踪信息，就将它们保存在受保护的审计存储中。

## 选择供应商要求的方案，然后限制影响范围

你很少能决定第三方 API 使用哪种身份验证方案，因为供应商已经做出了选择。但你可以决定凭据的权限范围是宽还是窄、存放在哪里、哪些请求可以使用它，以及智能体行为异常时如何处理。

设计边界时可以参考下面的比较：

| 模式 | 客户端发送的内容 | 被复制后的主要风险 | 合理的智能体处理方式 |
|---|---|---|---|
| Bearer token | `Authorization` 中的令牌 | 接收者可以直接重放 | 保存在执行器中，并限制权限范围和有效期 |
| Basic 身份验证 | 用户名和密码或令牌的 Base64 编码 | 解码后可以直接重放 | 在执行器中使用专用服务身份 |
| 静态自定义标头 | 供应商定义的密钥标头 | 通常可以直接重放 | 只为已批准的主机和路径注入 |
| 签名自定义标头 | 签名、时间戳和请求数据 | 重用可能失败，但签名材料仍然敏感 | 将签名密钥和规范化逻辑保留在执行器中 |

对签名请求要作出重要限定。时间戳和随机数可以减少 API 边界上的直接重放，但不会让签名密钥适合进入智能体上下文。能够访问密钥的智能体可以签署新的恶意请求。如果签名实现接受智能体提供的任意方法、主机、路径和请求体，它就会忠实地为你本来不想授权的操作签名。

权限范围应与操作匹配，而不是为想象中的未来用途预留。发布智能体可能只需要在一个项目中创建发布的权限，但不需要删除项目、修改账单、读取所有构件或邀请用户。如果供应商无法提供足够受限的凭据，就在供应商 API 前部署一个由你控制的、更窄的服务，或对危险调用保留人工审批。

不要用自然语言写一长串允许列表来弥补权限范围过宽的问题。“只能用这个令牌发布”是建议，不是执行点。应在组装请求的地方限制预期来源、允许的方法、路径模式、标头集合、请求体结构和最大响应大小。这些约束能让凭据离开预期任务后变得不那么有用。

## 中介机制让密钥远离智能体上下文

经过中介的 HTTP 调用只有在密钥持有者执行请求，而不是返回密钥让智能体执行时，才真正有效。两种设计都可能向智能体提供名为 `http_request` 的工具，因此很容易混淆。数据流才说明你实际构建的是哪一种设计。

在不安全的设计中，工具会获取令牌并交给智能体，可能是环境变量、占位符替换，或所谓的“临时”凭据。智能体的下一步操作会发送请求。此时令牌已经进入一个专门用于推理、转换和重复文本的系统。

在经过中介的设计中，智能体向执行器发送结构化请求。执行器检查请求是否符合允许的形状，从受保护的存储中取出选定凭据，注入正确的身份验证材料，发送请求，记录操作，并返回受限结果。智能体永远不会获得密钥值、密钥的编码形式，或包含密钥的 Shell 命令。

这个差异也会改变事件响应方式。如果怀疑某次智能体会话出了问题，可以先停止它请求操作的能力，而不必立即轮换所有凭据。如果凭据本身可能已经泄露，仍然要轮换。会话撤销和凭据轮换解决的是不同问题，把它们当成同一个按钮会浪费团队时间。

实际的执行器默认应拒绝以下几类请求：

- 指向未批准来源的绝对 URL，包括相似的仿冒子域名。
- 由用户提供的 `Authorization`、`Cookie`、代理或特定凭据标头。
- 可能将经过身份验证的请求带到其他来源的重定向。
- 超出凭据预期用途的方法，尤其是破坏性方法。
- 超出预期大小，或不符合端点预期格式的请求体。

Sallyport 在 macOS 上采用这种保管模式：其加密保险库存放 API 和 SSH 凭据，而 MCP 智能体请求应用执行 HTTP 调用，而不是接收凭据值。

不要把中介机制误认为通用策略引擎。它无法判断“删除过期测试资源”在某个生产账户中是否正确。它可以确保请求处于定义好的技术边界内，并确保凭据留在智能体上下文之外。人工审查、权限受限的账户和特定应用的保护措施，仍然决定该操作本身是否值得批准。

## 请求边界必须涵盖重定向、DNS 和响应

仅批准 `https://api.example.test` 仍然过于宽泛，因为带凭据的请求不只有主机名。执行器需要控制智能体能够改变有效目标或请求含义的每个位置。

从精确来源开始，也就是协议、主机名和端口。对于普通互联网 API 凭据，要求使用 HTTPS。RFC 9110 定义了 HTTP 请求目标和权限规则，但应用代码仍需执行自己的目标规则。不要只按后缀批准主机。类似“主机名以 `example.test` 结尾”的检查可能会接受 `notexample.test`，宽松的子字符串检查更危险。应将解析后的主机名与精确允许列表进行比较，或使用经过有意设计的子域名规则。

然后限制方法和路径。如果智能体应该创建发布，就只允许它需要的精确 `POST` 路径范围。不要因为清理操作可能有用就加入 `DELETE`。不要允许任意版本化路径，除非你已经确认后续 API 版本不会带来不同的行为。路径规范化同样重要。比较前先解析 URL；如果匹配代码无法稳定处理，就拒绝异常编码、点号路径段或重复分隔符。

必须明确处理重定向。不同 HTTP 库的行为不同，库升级也可能改变默认设置。对于带凭据的调用，最安全的默认做法是拒绝重定向，并把目标位置报告给智能体。如果供应商确实需要重定向，只允许指定的目标来源，并在同样的限制下重新构造请求。不要假设客户端会始终一致地移除凭据，从而认为开放重定向没有危害。

DNS 会带来第二个目标检查。受信任的主机名可能解析到不断变化的地址，而内部系统的名称可能指向敏感网络。如果执行器运行在开发者的笔记本电脑上，任意出站 HTTP 都可能成为访问本地管理服务或云元数据端点的路径。在建立连接前限制批准的来源，不要接受智能体控制的代理设置，也不要让智能体选择网络接口或解析器。

最后，要限制并过滤响应。智能体不需要下载数 MB 的响应，只为确认发布创建成功。大型响应会浪费上下文，还可能把不受信任服务中的指令带进智能体的推理过程。可以时，只返回下一步操作所需的字段。在工具契约中将远程文本标记为数据，并且绝不能让响应内容改写执行器的授权规则。

## 审批应说明调用进程和具体操作

只有在人类获得足够信息来做决定时，审批点击才有价值。“允许智能体访问 API”是伪装成提示的宽泛权限。它会导致审批疲劳，因为用户无法判断是哪个本地进程发起请求、请求使用哪个凭据，或它将发送什么内容。

应以能帮助操作员识别的方式标明调用进程。在 macOS 上，代码签名者通常比可变的进程名称更有用。名为 `agent` 的进程可能是合法的开发工具，也可能是不相关的、恰好使用同名的二进制文件。进程谱系、可执行文件路径和签名信息能提供更好的证据，但它们都不能替代有边界的操作请求。

会话审批和请求审批解决的是不同的取舍。会话审批可以减少对已知智能体运行的重复打扰，适合凭据和请求边界都很窄的低风险、重复性操作。请求审批适合不可逆或敏感的操作，例如对外发布、修改访问设置，或写入会触发其他系统的数据。

不要让用户每次调用都审查一份密集的原始 HTTP 转储。第三次被打断后，人们就会不看内容直接批准。应显示简明的操作摘要：服务身份、方法、目标、路径、有意义的请求体字段，以及 API 文档说明的副作用。原始请求细节可以保留在审计记录中，供日后调查。

还应区分启动会话的权限和保持会话存活的权限。如果智能体进程退出，替代进程不应仅因为名称相同就继承审批。如果用户撤销会话，执行器必须立即停止接受来自该会话的调用。界面显示“已撤销”，但已经获得授权的客户端仍能继续发送请求，这比没有撤销功能更糟，因为它会制造虚假的安全感。

## 审计记录必须回答智能体退出后发生了什么

有用的审计轨迹应让操作员回答：谁请求了调用、执行器使用了哪个凭据引用、请求发往哪里、执行器允许了什么，以及远程服务返回了什么。回答这些问题不需要记录原始密钥。事实上，记录密钥会创建第二个伪装成可观测性的密钥存储。

每次调用都应保留会话或进程身份、时间戳、选定的凭据引用、HTTP 方法、批准的来源、路径、相关请求数据的受保护表示、响应状态，以及允许或拒绝该请求的决定。若允许重定向，还要记录重定向后的实际最终目标。失败也要记录。针对异常路径反复尝试被拒绝的请求，往往能暴露出有问题的智能体指令或试图越过边界的行为。

防篡改证据会改变你对记录的信任程度。哈希链日志将每条记录与之前的记录关联起来，因此可以在验证时发现后续修改或删除。它不能证明执行器当初做出了明智的授权选择，也不能让已被攻陷的主机变得可信。但它确实会让悄悄修改历史记录变得困难，而这正是事件审查需要的特性。

审计记录应与智能体的普通工作上下文分开。智能体可以收到类似 `201 Created, release id r-4821` 的摘要。操作员可能需要包含路径和决策元数据的更完整记录。不能因为存在日志，就把原始授权标头提供给任何一方。

在 Sallyport 中，Sessions 和 Activity 日志都来自加密的哈希链审计日志，而 `sp audit verify` 可以在离线状态下验证该链，无需解锁保险库。这种设计适合审查，因为验证过程不需要暴露授权调用的凭据。

## 在授予生产访问权限前测试失败路径

只能在正常路径上成功的凭据边界，还没有资格进入生产环境。建立一个小型测试 API，或使用非生产账户，然后让执行器证明它会拒绝那些可能泄露或滥用凭据的情况。

可以按下面的顺序测试：

1. 请求一个已批准的 `GET` 端点，确认智能体能收到预期请求体，但收不到包含凭据的标头。
2. 由智能体提供 `Authorization` 标头，确认执行器拒绝请求，而不是悄悄合并或替换标头。
3. 将 URL 改为未批准的主机，再改为具有欺骗性的相似主机，确认两者都在发出网络请求前失败。
4. 让测试 API 返回跨来源的 `302` 响应，确认执行器停止请求，而不是继续转发身份验证信息。
5. 在运行期间撤销智能体会话，确认后续调用失败，同时审计验证仍然成功。

测试完成后，检查进程环境、临时目录、Shell 历史记录、生成的文件、崩溃报告和测试日志。搜索已知的测试凭据字符串。这个练习能发现数量惊人的包装脚本和调试模式泄露，而这些问题通常不会在请求级测试中出现。

还要测试供应商错误。API 可能返回回显无效标头内容的请求体、追踪标识符，或要求改用其他端点的建议。确认结果过滤器不会把类似凭据的材料交还给智能体，也确认重试逻辑不会把一次被拒绝的请求变成大量尝试。只有在 API 文档明确说明适合重试的错误上才重试，并保持方法和目标不变。

第一个生产凭据的权限范围应窄到这样的程度：测试失败只会带来不便，而不会造成灾难。如果团队无法准确说明它授权了哪些请求形状，也无法说明如何撤销正在运行的智能体任务，那么这个凭据对于自主使用来说仍然过于宽泛。
