凭据代理 — 技术
代理的工作原理。
它能防护什么。不能防护什么。
本页面向安全审查员、渗透测试人员以及评估代理威胁模型的工程师。它描述了代理在协议级别的作用、凭据在内存中的存在位置,以及仍然存在的攻击面。
架构
该代理是一个基于 CONNECT 的 HTTPS MITM 代理。AI 智能体将 HTTPS_PROXY 设置为指向它。当智能体发出 HTTPS 请求时,代理会拦截 TLS 连接,检查请求标头中的凭据引用,针对 Clavitor 保管库解析它们,并将注入了凭据的请求转发到上游 API。
默认情况下,代理监听 127.0.0.1:1983 — 即代理和智能体共享主机的 Sidecar 模式。对于共享部署(一个代理在专用网络上为多个智能体提供服务、作为多个工作负载的容器 Sidecar、专用代理主机),可以通过 CLAVITOR_PROXY_LISTEN 配置监听接口。
该代理是一个独立的 Go 二进制文件。无 CGO。所有 Clavitor 协议加密操作均经由编译为 WebAssembly 并在启动时通过 wazero 加载的规范 Rust 实现进行处理。
TLS 处理
代理在首次运行时生成一个自签名的 ECDSA P-256 根 CA,并以 0600 权限持久化在二进制文件所在目录中。对于每个上游主机,按需生成叶证书,由此 CA 签名,并缓存在内存中,采用有上限的驱逐机制(1,000 个主机)。叶证书的有效期为 24 小时,并在第 23 小时无感重新生成,以防止在会话中途过期。
智能体必须信任代理的 CA 证书。使用 clavitor-proxy ca 导出它。
上游连接至少使用 TLS 1.3,并通过 ALPN 协商 HTTP/2 和 HTTP/1.1。使用系统证书池进行上游验证。无证书固定 — 代理信任操作系统信任的任何证书。
凭据从不缓存,从不写入磁盘,且保留时间绝不超过一个 HTTP 请求。
| 阶段 | 凭据存在位置 | 持续时间 |
|---|---|---|
| 保管库中的静态数据 | 保管库数据库中的 AES-GCM 密文 | 直到删除 |
| 传输至代理途中 | 来自保管库 API 的 TLS 加密 JSON 响应 | 一次 HTTP 往返 |
| 在代理中解密 | 进程内存(堆上的 Go 字符串) | 一个 HTTP 请求 |
| 注入到上游请求中 | 传输至上游线路上的 TLS 加密字节 | 一个 HTTP 请求 |
代理在其整个运行期间将智能体的凭据解密密钥(16 字节)保存在内存中。它在启动时从加密的 Sidecar 配置(CLV1 格式)加载,并在正常关闭时清除。该密钥绝不会离开进程。
Sidecar 配置使用 AES-128-GCM 和 HMAC-SHA256 进行加密,并使用从静态种子派生的确定性密钥。这是混淆,而非机密性 — 安全边界是文件权限(0600)和对文件的持有。CLV1 格式在代理、CLI 和浏览器扩展之间共享。
解析模式
模式 1 — 显式占位符
智能体在请求标头中包含 clavitor://Entry/field 引用。代理按名称在保管库中搜索该条目,获取它,解密指定字段,并用真实值替换占位符。
如果搜索返回零个或多个结果,代理将返回带有稳定错误代码的 502。占位符绝不会被移除并原样转发。
模式 2 — URL 匹配
当不存在占位符时,代理会向保管库请求 URL 字段与上游主机匹配的条目。如果恰好存在一个具有已识别字段结构的匹配项,代理将自动注入凭据。
零个匹配 → 直通(无凭据预期)。多个匹配 → 带有消歧指导的 502。未知字段形状 → 502。
决策树是确定性的:存在占位符 → 解析或失败。无占位符 → URL 匹配或直通。不存在静默回退路径,即解析失败导致请求在没有凭据的情况下发送到上游。
默认情况下,保管库在每个请求中识别到代理自身的智能体 ID。速率限制、作用域检查和审计条目均归属于该代理。
当多个智能体共享一个代理实例时,占位符可以包含智能体 ID:clavitor://agentid@Entry/field。代理将此智能体 ID 发送到保管库,保管库应用该智能体的作用域和速率限制,并记录其访问日志。智能体 ID 是保管库 UI 中智能体详细信息页面上显示的 32 字符十六进制值。
# Without agent ID — attributed to the proxy Authorization: Bearer clavitor://OpenAI/key # With agent ID — attributed to agent 0102030405060708090a0b0c0d0e0f10 Authorization: Bearer clavitor://0102030405060708090a0b0c0d0e0f10@OpenAI/key
| 部署 | 身份模型 | 隔离 |
|---|---|---|
| 每个智能体一个代理 | 代理 ID = 智能体 ID(默认) | 完全 — 独立的二进制文件、配置、作用域、速率限制 |
| 共享代理,URL 中无智能体 ID | 所有智能体共享代理的 ID | 共享作用域和速率限制 |
共享代理 + URL 中的 agentid@ | 每智能体身份 | 每智能体作用域、速率限制和审计 |
URL 中的智能体 ID 不是身份验证机制 — 代理的 CVT 令牌对连接进行身份验证。智能体 ID 决定归属:应用谁的作用域、计算谁的速率限制、谁的审计跟踪记录该访问。保管库会以显式失败拒绝未知的智能体 ID。
网络安全
SSRF 防护
默认情况下,代理阻止发往专用网络(RFC 1918)、云实例元数据(169.254.169.254)、环回、链路本地和运营商级 NAT 范围的上游连接。首先解析 DNS;在建立 TCP 连接之前验证所有返回的 IP,从而消除 DNS 重绑定 TOCTOU 窗口。
对于合法访问专用 API 的智能体,使用 CLAVITOR_PROXY_ALLOW_PRIVATE=true 进行覆盖。
目标固定
CONNECT 目标主机在隧道建立时被捕获,并在整个隧道生命周期内使用。隧道内的后续请求无法通过操作 Host 标头重定向到其他主机。不匹配会导致 502。
这可以防止智能体建立到 api.openai.com 的隧道,然后向 internal-service.corp 发送请求。
标头处理
根据 RFC 7230 §6.1,逐跳标头从请求和响应中剥离:Connection、Keep-Alive、Proxy-Authenticate、Proxy-Authorization、Proxy-Connection、TE、Trailers、Transfer-Encoding、Upgrade。
从上游响应中剥离 Set-Cookie,以防止上游在智能体的 HTTP 客户端中植入 Cookie。
请求和响应主体以流式传输,无需缓冲。默认情况下,请求主体上限为 64 MB(CLAVITOR_PROXY_MAX_BODY_MB)。响应主体以流式传输,无硬性上限;当 Content-Length 超过 100 MB 时,会发出日志警告。
在 URL 匹配模式下,代理将保管库字段标签映射到 HTTP 标头:
| 字段标签 | 注入的标头 |
|---|---|
key、apikey、api_key、token、secret、bearer、access_token | Authorization: Bearer <value> |
x-api-key、api-key | X-API-Key: <value> |
username + password(成对) | Authorization: Basic base64(user:pass) |
| 其他任何内容 | 已拒绝 — ERR-PROXY-052 |
在占位符模式下,智能体控制解析哪个字段以及它的去向。上述映射仅适用于 URL 匹配模式。
每次失败都会生成一个稳定的 ERR-PROXY-NNN 代码。这些代码是代理公共接口的一部分 — 智能体和操作员可以基于这些代码进行匹配,以触发警报和进行调试。
| 范围 | 类别 |
|---|---|
001–019 | 设置(配置、初始化、CA 生成、WASM) |
020–029 | 守护进程生命周期 |
030–049 | 占位符解析(clavitor:// URI) |
050–069 | URL 匹配注入 |
070–089 | 上游 / TLS |
身份字段不可达
保管库条目支持三个加密级别。保管库加密字段是明文元数据。凭据加密字段使用智能体的密钥解密。身份加密字段使用服务器和代理从未见过的密钥加密。只有保管库所有者通过其硬件密钥才能解密它们。
如果占位符引用了身份加密字段,代理将返回 ERR-PROXY-035。无回退机制,无部分结果。该字段在架构上对代理不可达。
代理无法防护的内容
代理的威胁模型是被攻陷的技能或提示注入,导致经过身份验证的智能体收集凭据。保管库的每智能体速率限制、唯一条目配额和两次违规锁定是主要防御措施。代理增加了一个网络层执行点,在此处按请求解析凭据,且智能体从不持有凭据。
代理与智能体在同一台机器上运行。拥有 root 访问权限的攻击者可以读取进程内存、附加调试器或拦截环回流量。代理是一个凭据注入层,而不是硬件安全边界。
如果上游 API 在其响应中回显凭据(例如“whoami”端点),智能体会看到该凭据。代理将凭据注入请求,而不是响应。它不过滤返回的内容。
日志记录
代理为每个接受的 CONNECT 请求记录一行日志,并为失败输出错误行。它绝不记录:
- 解密的凭据值
- 完整的请求 URL(查询字符串可能携带机密 — 仅记录方案 + 主机 + 路径)
- 请求或响应主体
- 凭据解密密钥或 Sidecar 配置内容
当代理检测到来自上游的 400 响应包含与身份验证相关的关键词(unauthorized、invalid token 等)时,它会记录一条诊断提示,表明注入的凭据可能已失效。响应将原样转发。
加密
所有 Clavitor 协议加密操作 — AES-GCM 字段解密、HKDF 密钥派生、base62 编码、CVT 令牌生成、CLV1 配置打包/解包 — 均在通过 wazero(一个纯 Go WASM 运行时)加载的单个 WebAssembly 模块(clavis_crypto.wasm)内执行。无 CGO。无 Clavitor 原语的 Go 重新实现。
WASM 模块是从浏览器、CLI 和浏览器扩展使用的同一个 Rust crate(clavis-crypto)编译的。单一事实来源,单一二进制文件,单一审计面。
代理使用 Go 的 crypto/tls 处理 TLS 传输,并使用 crypto/ecdsa 生成 MITM 证书。这些属于传输层事项,而非 Clavitor 协议操作。
机密(凭据解密密钥、智能体 ID、设备 ID、保管库 URL)存在于加密的 CLV1 Sidecar 配置中,在 clavitor-proxy init 期间写入一次。操作旋钮存在于环境变量中:
| 变量 | 默认值 | 用途 |
|---|---|---|
CLAVITOR_PROXY_LISTEN | 127.0.0.1 | 监听接口。对于共享部署设置为 0.0.0.0,或设置为特定的接口 IP。 |
CLAVITOR_PROXY_PORT | 1983 | 监听端口 |
CLAVITOR_PROXY_ALLOW_PRIVATE | false | 允许连接到 RFC-1918 / 专用网络 |
CLAVITOR_PROXY_MAX_BODY_MB | 64 | 请求主体大小上限 |
CLAVITOR_PROXY_WRITE_TIMEOUT | 300 | 响应写入超时(秒) |
CLAVITOR_CONFIG | (exe 目录) | 覆盖 Sidecar 配置路径 |
操作旋钮不是机密。它们不属于加密配置。它们属于部署工具已经管理它们的地方 — 环境。
请您自行审查。
加密是一个单一的可审计 WASM 构件。威胁模型已记录在案。如果您发现我们遗漏的内容,我们希望听取您的反馈。