summary: "基于 OpenClaw 源码的深度安全审计,覆盖 API keys、tokens、OAuth 凭证、配置脱敏、日志安全、节点通信认证、Session 数据存储等 7 大安全维度,包含具体源码引用和安全建议。"
OpenClaw 敏感信息安全分析(源码审计)
> 审计日期:2026-04-04
> 源码版本:GitHub open-claw/openclaw main 分支
> 审计范围:敏感信息存储、传输、脱敏、运行时管理
目录
1. 概述
2. 配置中的敏感信息存储
3. 传输安全
4. Session 数据存储
5. Node 通信安全
7. 日志安全与脱敏
9. 安全亮点总结
10. 安全风险与建议
1. 概述
OpenClaw 是一个 AI Agent 网关平台,需要管理大量敏感信息:LLM Provider API Keys、Channel Credentials(Discord/Telegram/飞书 tokens)、OAuth 凭证、Gateway 认证令牌等。本报告基于源码审计,分析其安全实践。
总体评价:OpenClaw 在敏感信息处理方面展现了较高的安全意识,实现了多层防护机制,包括 SecretRef 间接引用体系、配置脱敏系统、日志敏感信息自动遮蔽、常数时间密钥比较等。但也存在一些值得关注的改进空间。
2. 配置中的敏感信息存储
2.1 SecretRef 引用体系(核心机制)
OpenClaw 设计了一套 SecretRef 间接引用系统,避免在配置文件中直接存放明文密钥。
源码文件:src/config/types.secrets.ts
// 三种 secret 来源
export type SecretRefSource = "env" | "file" | "exec";
export type SecretRef = {
source: SecretRefSource;
provider: string;
id: string;
};
三种 Secret Provider 类型:
| Provider 类型 | 来源 | 配置类型 |
|---|---|---|
| `env` | 环境变量 | `EnvSecretProviderConfig` — 支持 allowlist 限制可访问的环境变量 |
| `file` | 文件(单值或 JSON) | `FileSecretProviderConfig` — 支持 `singleValue` / `json` 模式 |
| `exec` | 外部命令执行 | `ExecSecretProviderConfig` — 支持 Vault 等外部密钥管理器 |
支持 ${ENV_VAR} 模板语法(src/config/types.secrets.ts:50-62):
const ENV_SECRET_TEMPLATE_RE = /^\$\{([A-Z][A-Z0-9_]{0,127})\}$/;
配置中可写 "${OPENAI_API_KEY}" 而非明文,运行时从环境变量解析。
2.2 Secret 解析引擎
源码文件:src/secrets/resolve.ts
解析引擎支持:
- 并发控制:
maxProviderConcurrency(默认 4)限制同时解析的 provider 数量 - 批次限制:
maxRefsPerProvider(默认 512)、maxBatchBytes(默认 256KB) - 缓存机制:
SecretRefResolveCache避免重复解析相同的 secret - 错误隔离:
SecretProviderResolutionError和SecretRefResolutionError分离 provider 级别和 ref 级别的错误
2.3 安全文件读取
源码文件:src/infra/secret-file.ts
export function loadSecretFileSync(filePath, label, options) {
// 1. 路径解析 & 验证
const resolvedPath = resolveUserPath(trimmedPath);
// 2. 符号链接检查(可选拒绝)
if (options.rejectSymlink && previewStat.isSymbolicLink()) { ... }
// 3. 大小限制(默认 16KB)
if (previewStat.size > maxBytes) { ... }
// 4. 安全打开验证
const opened = openVerifiedFileSync({ ... });
}
src/acp/secret-file.ts 进一步强制:
export function readSecretFromFile(filePath, label) {
return readSecretFileSync(filePath, label, {
maxBytes: MAX_SECRET_FILE_BYTES,
rejectSymlink: true, // 强制拒绝符号链接
});
}
TOCTOU 防护(src/infra/safe-open-sync.ts):
openVerifiedFileSync()在打开文件后通过O_NOFOLLOW标志和fstat二次验证,防止竞态条件攻击- 检查文件硬链接数量(可选)
2.4 Exec Secret Provider 安全策略
源码文件:src/secrets/resolve.ts
Exec provider 可配置:
trustedDirs:限制命令只能从受信目录执行allowInsecurePath/allowSymlinkCommand:默认拒绝不安全路径和符号链接命令timeoutMs/noOutputTimeoutMs:超时保护maxOutputBytes:输出大小限制jsonOnly:限制只接受 JSON 输出passEnv:控制传递给命令的环境变量
2.5 配置文件权限
源码文件:src/config/io.ts、src/secrets/shared.ts:58-63
// 原子写入 + 严格权限
export function writeTextFileAtomic(pathname, value, mode = 0o600) {
ensureDirForFile(pathname);
const tempPath = `${pathname}.tmp-${process.pid}-${Date.now()}`;
fs.writeFileSync(tempPath, value, "utf8");
fs.chmodSync(tempPath, mode); // owner-only 读写
fs.renameSync(tempPath, pathname);
}
配置文件默认使用 0o600(仅所有者可读写)。备份文件同样限制权限(src/config/backup-rotation.ts)。
2.6 Secret 审计系统
源码文件:src/secrets/audit.ts
OpenClaw 内置了 Secret 审计机制,可以检测:
| 审计代码 | 说明 |
|---|---|
| `PLAINTEXT_FOUND` | 发现明文存储的密钥 |
| `REF_UNRESOLVED` | SecretRef 无法解析 |
| `REF_SHADOWED` | SecretRef 被明文值覆盖 |
| `LEGACY_RESIDUE` | 存在遗留的明文凭证 |
这为用户提供了主动发现安全问题的能力。
3. 传输安全
3.1 Gateway TLS 支持
源码文件:src/infra/tls/gateway.ts、src/gateway/server/tls.ts
Gateway 原生支持 TLS:
- 自动生成自签名证书:RSA 2048-bit,SHA256 签名,有效期 10 年
- 证书文件权限:
0o600(仅所有者可读写) - 证书指纹追踪:SHA256 fingerprint 用于连接验证
- 协议选择:启用 TLS 时自动使用
wss://
// src/infra/tls/gateway.ts:45-62
async function generateSelfSignedCert(params) {
await execFileAsync(opensslBin, [
"req", "-x509", "-newkey", "rsa:2048", "-sha256",
"-days", "3650", "-nodes",
"-keyout", params.keyPath, "-out", params.certPath,
"-subj", "/CN=openclaw-gateway",
]);
await fs.chmod(params.keyPath, 0o600);
await fs.chmod(params.certPath, 0o600);
}
3.2 Tailscale 集成
源码文件:src/gateway/server-tailscale.ts、src/gateway/connection-details.ts
支持 Tailscale Serve/Funnel 模式提供零配置 HTTPS 远程访问,无需手动管理证书。
3.3 与外部 LLM Provider 的通信
对外部 API 调用(OpenAI、Anthropic 等)通过 HTTPS 进行,这由各 provider SDK 的默认行为保证。OpenClaw 本身不降级这些连接。
3.4 注意事项
- 默认状态:Gateway 默认监听 HTTP(非 HTTPS),需要手动启用 TLS 或使用 Tailscale
- 本地开发:
connection-details.ts:85提示用户 "use Tailscale Serve/Funnel for HTTPS remote access",说明默认不强制 TLS - 自签名证书:自动生成的是自签名证书,客户端需要信任或跳过验证
4. Session 数据存储
4.1 存储格式与位置
源码文件:src/config/sessions/transcript.ts、src/agents/session-dirs.ts
Session 数据以 JSONL(换行分隔 JSON)格式存储:
{state_dir}/agents/{agent_id}/sessions/{session_file}.jsonl
// src/config/sessions/transcript.ts:29-37
async function ensureSessionHeader(params) {
const header = {
type: "session", version: CURRENT_SESSION_VERSION,
id: params.sessionId, timestamp: new Date().toISOString(),
cwd: process.cwd(),
};
await fs.promises.writeFile(params.sessionFile, `${JSON.stringify(header)}\n`, {
encoding: "utf-8",
mode: 0o600, // 严格权限
});
}
4.2 文件系统安全
- Session 文件权限:
0o600(仅所有者可读写) - Session Store 文件(
src/config/sessions/store.ts)同样使用原子写入 +0o600权限 - 文件修复机制(
src/agents/session-file-repair.ts):自动检测和修复损坏的 session 文件
4.3 风险点
- 明文存储:对话历史以明文 JSON 存储在磁盘上,不进行加密
- 依赖文件系统权限:安全性完全依赖于 OS 级文件权限和用户账户隔离
- 无自动过期清理:Session 文件不会自动过期删除(需手动管理)
5. Node 通信安全
5.1 配对令牌机制
源码文件:src/infra/pairing-token.ts
export const PAIRING_TOKEN_BYTES = 32;
export function generatePairingToken(): string {
return randomBytes(PAIRING_TOKEN_BYTES).toString("base64url");
}
export function verifyPairingToken(provided, expected): boolean {
if (provided.trim().length === 0 || expected.trim().length === 0) return false;
return safeEqualSecret(provided, expected);
}
- 256 位随机令牌:使用
crypto.randomBytes(32)生成 - 常数时间比较:使用
safeEqualSecret()防止时序攻击
5.2 设备认证
源码文件:src/gateway/device-auth.ts、src/infra/device-auth-store.ts
设备认证使用签名 payload 机制:
// v3 payload 格式
function buildDeviceAuthPayloadV3(params) {
return [
"v3", params.deviceId, params.clientId, params.clientMode,
params.role, scopes, String(params.signedAtMs), token,
params.nonce, platform, deviceFamily,
].join("|");
}
HMAC 签名(src/infra/exec-host.ts):
const hmac = crypto.createHmac("sha256", token).update(payload).digest("hex");
设备认证存储(src/infra/device-auth-store.ts:42-48):
function writeStore(filePath, store) {
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, JSON.stringify(store, null, 2) + "\n", { mode: 0o600 });
fs.chmodSync(filePath, 0o600);
}
5.3 安全特性
- Nonce 防重放:每次认证包含随机 nonce
- 时间戳验证:
signedAtMs用于检测过期认证请求 - 基于角色的权限控制:
role和scopes限制设备能力 - HMAC-SHA256 签名:payload 完整性保护
6. OAuth Tokens 管理
6.1 Auth Profile 存储
源码文件:src/agents/auth-profiles/types.ts、src/agents/auth-profiles/store.ts
OpenClaw 支持三种认证 Profile 类型:
| 类型 | 存储内容 | 典型用途 |
|---|---|---|
| `api_key` | API Key(或 SecretRef) | OpenAI、Anthropic 等 |
| `oauth` | access + refresh token + 过期时间 | 飞书、GitHub 等 |
| `token` | 静态 Bearer Token | PAT 类凭证 |
export type OAuthCredentials = {
access: string; // access token
refresh: string; // refresh token
expires: number; // 过期时间戳
provider?: OAuthProvider;
email?: string;
};
6.2 OAuth 安全实践
PKCE 支持(src/plugin-sdk/oauth-utils.ts):
export function generatePkceVerifierChallenge() {
const verifier = randomBytes(32).toString("base64url");
const challenge = createHash("sha256").update(verifier).digest("base64url");
return { verifier, challenge };
}
6.3 外部 CLI 凭证同步
源码文件:src/agents/cli-credentials.ts
OpenClaw 可以从外部 CLI 工具(Claude CLI、Codex CLI)的系统 Keychain 读取凭证:
readClaudeCliKeychainCredentials()
readCodexKeychainCredentials()
6.4 Auth Store 文件保护
Auth Store 同样使用 JSON 格式存储,位于 state 目录下,受文件权限保护。pi-auth-json.ts 显示写入时使用 0o600 模式。
6.5 风险点
- OAuth Token 明文存储:access/refresh token 以明文 JSON 存储在磁盘
- ApiKeyCredential 的
key字段:虽然支持keyRef(SecretRef 间接引用),但仍支持直接存储key字符串 - Token 刷新安全:由
managedBy标记管理,但跨进程刷新可能存在竞态
7. 日志安全与脱敏
7.1 日志敏感信息自动遮蔽
源码文件:src/logging/redact.ts
这是 OpenClaw 安全设计中的一个亮点。日志系统内置了正则表达式驱动的自动脱敏:
const DEFAULT_REDACT_PATTERNS = [
// 环境变量赋值
String.raw`\b[A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD|PASSWD)\b\s*[=:]\s*(["']?)([^\s"'\\]+)\1`,
// JSON 字段
String.raw`"(?:apiKey|token|secret|password|...)"\s*:\s*"([^"]+)"`,
// CLI 参数
String.raw`--(?:api[-_]?key|token|secret|password|passwd)\s+(["']?)([^\s"']+)\1`,
// Authorization 头
String.raw`Authorization\s*[:=]\s*Bearer\s+([A-Za-z0-9._\-+=]+)`,
// 常见 token 前缀
String.raw`\b(sk-[A-Za-z0-9_-]{8,})\b`, // OpenAI
String.raw`\b(ghp_[A-Za-z0-9]{20,})\b`, // GitHub
String.raw`\b(github_pat_...)`, // GitHub PAT
String.raw`\b(xox[baprs]-...)`, // Slack
String.raw`\b(AIza[0-9A-Za-z\-_]{20,})\b`, // Google
String.raw`\b(pplx-...)`, // Perplexity
String.raw`\b(npm_...)`, // npm
String.raw`\bbot(\d{6,}:[A-Za-z0-9_-]{20,})`, // Telegram Bot
// PEM 私钥块
String.raw`-----BEGIN [A-Z ]*PRIVATE KEY-----...`,
];
遮蔽策略(保留首尾用于调试):
function maskToken(token) {
if (token.length < 18) return "***";
const start = token.slice(0, 6);
const end = token.slice(-4);
return `${start}…${end}`;
}
7.2 诊断 Payload 脱敏
源码文件:src/agents/payload-redaction.ts
专门的诊断数据脱敏器,两个级别:
1. redactImageDataForDiagnostics():将 base64 图片数据替换为 ,保留 SHA256 hash 和字节数
2. sanitizeDiagnosticPayload():额外移除凭证类字段
function isCredentialFieldName(key) {
const normalized = normalizeFieldName(key);
return (
normalized.endsWith("apikey") || normalized.endsWith("password") ||
normalized.endsWith("passwd") || normalized.endsWith("passphrase") ||
normalized.endsWith("secret") || normalized.endsWith("secretkey") ||
normalized.endsWith("token")
);
}
7.3 WebSocket 日志脱敏
源码文件:src/gateway/ws-log.ts
WebSocket 通信日志自动应用脱敏模式:
const WS_LOG_REDACT_OPTIONS = {
mode: "tools" as const,
patterns: getDefaultRedactPatterns(),
};
// 每条 WS 日志消息自动过滤
const redacted = redactSensitiveText(str, WS_LOG_REDACT_OPTIONS);
7.4 配置可控
日志脱敏支持配置开关:
type RedactSensitiveMode = "off" | "tools";
// 默认模式:"tools"(开启脱敏)
用户可以通过 logging.redactSensitive 配置项控制,也可以自定义 logging.redactPatterns 正则表达式列表。
8. 运行时 Secrets 管理
8.1 配置快照脱敏
源码文件:src/config/redact-snapshot.ts
这是最复杂的安全组件之一。当配置需要暴露给 Web UI / API 时:
export const REDACTED_SENTINEL = "__OPENCLAW_REDACTED__";
export function redactConfigSnapshot(snapshot) {
// 1. 脱敏解析后的 config 对象
const redactedConfig = redactObject(snapshot.config, uiHints);
// 2. 脱敏 parsed 对象
const redactedParsed = snapshot.parsed ? redactObject(snapshot.parsed, uiHints) : null;
// 3. 脱敏原始 JSON5 文本
let redactedRaw = snapshot.raw ? redactRawText(snapshot.raw, snapshot.config, uiHints) : null;
// 4. 脱敏 resolved 配置(包含 ${ENV} 替换后的值)
const redactedResolved = redactConfigObject(snapshot.resolved, uiHints);
}
关键设计:
- 使用
__OPENCLAW_REDACTED__哨兵值替换敏感数据 - 往返安全:
restoreRedactedValues()在写入时恢复原始值,避免 Web UI 编辑导致凭证丢失 - 无效配置保护:配置解析失败时,拒绝输出 raw 文本(防止未脱敏的配置泄露)
- 双重脱敏:同时脱敏 config object 和 raw JSON5 source
8.2 敏感路径检测
源码文件:src/config/schema.hints.ts:136-154
基于正则表达式的路径敏感性检测:
const SENSITIVE_PATTERNS = [
/token$/i, /password/i, /secret/i,
/api.?key/i, /encrypt.?key/i, /private.?key/i,
/serviceaccount(?:ref)?$/i,
];
同时维护白名单避免误报:
const SENSITIVE_KEY_WHITELIST_SUFFIXES = [
"maxOutputTokens", "maxInputTokens", "contextTokens",
"tokenCount", "tokenLimit", "tokenBudget", "passwordFile",
];
8.3 Zod Schema 敏感标记
源码文件:src/config/zod-schema.sensitive.ts
export const sensitive = z.registry<undefined, z.ZodType>();
通过 Zod registry 在 schema 层面标记敏感字段,确保脱敏逻辑与 schema 定义保持同步。
8.4 常数时间密钥比较
源码文件:src/security/secret-equal.ts
export function safeEqualSecret(provided, expected) {
if (typeof provided !== "string" || typeof expected !== "string") return false;
const hash = (s) => createHash("sha256").update(s).digest();
return timingSafeEqual(hash(provided), hash(expected));
}
使用 crypto.timingSafeEqual + SHA256 预哈希(解决长度不等时的时序泄露),防止时序攻击。
8.5 Gateway 认证启动
源码文件:src/gateway/startup-auth.ts
- 自动生成令牌:首次启动无 token 时自动生成
crypto.randomBytes(24).toString("hex")(192 位) - 令牌隔离:
assertHooksTokenSeparateFromGatewayAuth()确保 hooks token 与 gateway auth token 不同 - 持久化控制:生成的 token 仅在非 override 模式下持久化到配置文件
8.6 Auth 速率限制
源码文件:src/gateway/auth-rate-limit.ts(从 auth.ts 引用)
Gateway 认证支持速率限制,防止暴力破解。
9. 安全亮点总结
| 维度 | 评价 | 关键机制 |
|---|---|---|
| **Secret 存储** | ⭐⭐⭐⭐ | SecretRef 间接引用、env/file/exec 三种 provider |
| **配置脱敏** | ⭐⭐⭐⭐⭐ | 多层脱敏(config + raw + parsed + resolved)、哨兵值往返安全 |
| **日志安全** | ⭐⭐⭐⭐⭐ | 17+ 种正则模式自动脱敏、可配置 |
| **密钥比较** | ⭐⭐⭐⭐⭐ | SHA256 + timingSafeEqual 防时序攻击 |
| **文件权限** | ⭐⭐⭐⭐ | 默认 0o600、原子写入、symlink 检查 |
| **Node 认证** | ⭐⭐⭐⭐ | 256 位 pairing token、HMAC-SHA256、nonce 防重放 |
| **传输加密** | ⭐⭐⭐ | 支持 TLS 但默认不启用 |
| **Session 加密** | ⭐⭐ | 仅依赖文件权限,无数据加密 |
| **密钥审计** | ⭐⭐⭐⭐ | 内置明文检测和 SecretRef 健康检查 |
10. 安全风险与建议
🔴 高优先级
10.1 Session 对话历史明文存储
风险:对话历史(可能包含用户的敏感讨论内容、代码片段、API keys 等)以明文 JSONL 存储在磁盘上。
建议:
- 考虑实现可选的 Session 数据静态加密(at-rest encryption)
- 提供 Session 数据自动过期清理功能
- 在文档中明确告知用户 Session 数据的存储位置和安全注意事项
10.2 OAuth Token 明文持久化
风险:access 和 refresh token 以明文存储在 Auth Profile Store 中。Refresh token 尤其敏感(长期有效)。
建议:
- 利用系统 Keychain/Credential Manager 存储 OAuth tokens(已有 Keychain 读取能力,可扩展为写入)
- 或实现基于主密钥的本地加密存储
🟡 中优先级
10.3 TLS 默认未启用
风险:Gateway 默认以 HTTP 运行。在非 localhost 环境中,认证 token、对话内容可能被网络窃听。
建议:
- 在非 loopback 绑定时警告用户启用 TLS
- 考虑非 localhost 监听时默认强制 TLS
- 完善文档中的 TLS 配置指南
10.4 API Key 直接存储仍被允许
风险:ApiKeyCredential 类型仍支持直接存储 key 字符串(而非仅通过 keyRef),用户可能忽略 SecretRef 机制。
建议:
- 在配置验证中加入明文 API Key 的 warning(审计系统已有
PLAINTEXT_FOUND,可提升可见性) - 新用户引导流程中默认使用 SecretRef
🟢 低优先级
10.5 自签名证书安全
风险:自动生成的自签名证书使用 RSA 2048(安全但不前沿),且有效期 10 年较长。
建议:
- 考虑支持 Ed25519 或 ECDSA P-256 证书
- 缩短有效期并支持自动轮换
10.6 内存中的 Secret 生命周期
风险:解析后的 secret 值在 Node.js 内存中以普通字符串存在,GC 行为不可控,可能通过 heap dump 泄露。
建议:
- 这在 Node.js 生态中是普遍问题,短期无完美解决方案
- 考虑在不需要时主动清除 secret 变量引用
- 文档中提醒用户不要在生产环境中启用 heap dump
10.7 日志脱敏正则表达式覆盖范围
现状:已覆盖 OpenAI/GitHub/Slack/Google/Telegram 等主流 token 格式。
建议:
- 定期更新正则模式以覆盖新的 provider token 格式
- 考虑添加 Anthropic (
sk-ant-)、Cloudflare (cf-) 等新兴格式
附录:关键源码文件索引
| 文件 | 功能 |
|---|---|
| `src/config/types.secrets.ts` | SecretRef 类型定义与解析 |
| `src/secrets/resolve.ts` | Secret 值解析引擎 |
| `src/secrets/audit.ts` | Secret 安全审计 |
| `src/secrets/apply.ts` | Secret 配置应用 |
| `src/secrets/shared.ts` | 原子写入等工具函数 |
| `src/infra/secret-file.ts` | 安全文件读取 |
| `src/infra/safe-open-sync.ts` | TOCTOU 防护文件打开 |
| `src/infra/tls/gateway.ts` | TLS 证书管理 |
| `src/infra/pairing-token.ts` | Node 配对令牌 |
| `src/infra/device-auth-store.ts` | 设备认证存储 |
| `src/security/secret-equal.ts` | 常数时间密钥比较 |
| `src/security/audit-fs.ts` | 文件系统权限审计 |
| `src/logging/redact.ts` | 日志敏感信息脱敏 |
| `src/agents/payload-redaction.ts` | 诊断 payload 脱敏 |
| `src/config/redact-snapshot.ts` | 配置快照脱敏 |
| `src/config/schema.hints.ts` | 敏感路径检测 |
| `src/config/zod-schema.sensitive.ts` | Schema 层敏感标记 |
| `src/gateway/auth.ts` | Gateway 认证逻辑 |
| `src/gateway/startup-auth.ts` | Gateway 启动认证 |
| `src/gateway/ws-log.ts` | WebSocket 日志脱敏 |
| `src/gateway/device-auth.ts` | 设备认证协议 |
| `src/agents/auth-profiles/types.ts` | Auth Profile 类型定义 |
| `src/agents/auth-profiles/store.ts` | Auth Profile 存储管理 |
| `src/config/sessions/transcript.ts` | Session 转录存储 |
| `src/plugin-sdk/oauth-utils.ts` | OAuth PKCE 工具 |