主题
OpenClaw 源码解读(十二)安全系统
本篇深入分析 OpenClaw 的安全体系,涵盖认证授权、SSRF 防护、命令执行安全、设备身份、密钥管理、安全审计等核心机制。安全系统是 OpenClaw 最精密的工程之一,涉及 50+ 个源文件,代码量超过 8000 行。
目录
- 一、安全系统全景
- 二、认证与授权
- 三、设备身份系统
- 四、SSRF 防护链
- 五、命令执行安全
- 六、文件系统安全
- 七、密钥管理系统
- 八、DM 私信策略与配对
- 九、安全审计引擎
- 十、安全正则与 ReDoS 防护
- 十一、沙箱安全验证
- 十二、模块关系与设计总结
一、安全系统全景
1.1 安全哲学
OpenClaw 的安全设计遵循一个核心原则:安全的默认值 + 可选的能力放开。用户必须显式选择放开安全限制(opt-in),而非默认暴露后再关闭(opt-out)。
其信任模型的核心假设:
- 操作者信任:安装并运行 OpenClaw 的用户被视为可信
- 单用户设计:
main会话拥有主机的完全访问权限 - 入站不信任:来自外部消息渠道的 DM 默认视为不可信输入
- 深度防御:多层安全检查叠加,单一防线失效不会导致全面沦陷
1.2 安全子系统分布
安全系统
├── src/security/ ← 安全审计引擎 + DM 策略 + 危险标志
├── src/gateway/auth*.ts ← 网关认证(多模式)+ 频率限制
├── src/infra/device-*.ts ← 设备身份(Ed25519)+ Token 存储
├── src/infra/net/ssrf.ts ← SSRF 防护 + DNS 钉住
├── src/infra/exec-*.ts ← 命令执行审批 + 白名单 + 混淆检测
├── src/infra/fs-safe.ts ← 安全文件操作
├── src/infra/path-guards.ts ← 路径安全守卫
├── src/secrets/ ← 密钥解析 + 审计 + SecretRef
├── src/agents/sandbox/ ← Docker 沙箱安全验证
├── src/browser/ ← 浏览器导航守卫 + 控制面板认证
└── src/cli/security-cli.ts ← `openclaw security audit` 命令安全系统/01-infographic-security-overview-1775150767530.png)
二、认证与授权
2.1 时序安全的密钥比较 (src/security/secret-equal.ts)
这是整个认证体系的基石,仅 12 行代码,但蕴含了关键的安全原理:
typescript
import { createHash, timingSafeEqual } from "node:crypto";
export function safeEqualSecret(
provided: string | undefined | null,
expected: string | undefined | null,
): boolean {
if (typeof provided !== "string" || typeof expected !== "string") {
return false;
}
const hash = (s: string) => createHash("sha256").update(s).digest();
return timingSafeEqual(hash(provided), hash(expected));
}为什么需要这个函数?
普通的 === 比较会在发现第一个不匹配字符时立即返回 false。攻击者可以通过精确测量比较时间来逐字节推断正确的密钥——这就是时序攻击(Timing Attack)。
两层防护:
- SHA-256 哈希:将两个字符串都哈希为固定 32 字节,确保
timingSafeEqual的输入长度一致(timingSafeEqual要求两个 Buffer 等长) timingSafeEqual:Node.js 提供的恒定时间比较,无论输入如何,比较时间完全相同
2.2 网关多模式认证 (src/gateway/auth.ts)
Gateway 支持四种认证模式,覆盖从本地开发到生产部署的各种场景:
| 模式 | 适用场景 | 安全级别 |
|---|---|---|
none | 仅本地回环(127.0.0.1) | 最低 |
token | API 集成、设备连接 | 中 |
password | Funnel/远程访问 | 中高 |
trusted-proxy | 反向代理后(Nginx/Caddy) | 取决于代理 |
认证决策流程:
HTTP/WS 请求到达
│
├─ [trusted-proxy?] → 验证来源 IP + 代理头部 + 用户身份头部
│
├─ [none?] → 仅允许 loopback 地址,直接放行
│
├─ 速率限制检查 → 是否被锁定?
│
├─ [Tailscale?] → 调用 Tailscale Whois 验证身份
│
├─ [token?] → safeEqualSecret(请求token, 配置token)
│
├─ [password?] → safeEqualSecret(请求密码, 配置密码)
│
└─ → 返回 GatewayAuthResult { ok, role, identity }关键安全约束: 禁止明文 ws:// 连接到非 loopback 地址(CWE-319, CVSS 9.8)。
2.3 认证频率限制 (src/gateway/auth-rate-limit.ts)
基于内存的滑动窗口频率限制器,防止暴力破解攻击:
typescript
export function createAuthRateLimiter(config?: RateLimitConfig): AuthRateLimiter {
const maxAttempts = config?.maxAttempts ?? 10; // 默认 10 次
const windowMs = config?.windowMs ?? 60_000; // 1 分钟窗口
const lockoutMs = config?.lockoutMs ?? 300_000; // 锁定 5 分钟
const exemptLoopback = config?.exemptLoopback ?? true; // 本地回环豁免
// ...
}核心设计:
- 滑动窗口:不是简单的"每分钟 N 次",而是维护每次失败的时间戳数组,精确地滑动清理过期记录
- 作用域隔离:按
{scope, clientIp}独立计数,shared-secret、device-token、hook-auth 互不干扰 - 本地回环豁免:
127.0.0.1/::1永远不会被锁定,防止本地 CLI 被意外封锁 - 定期清理:60 秒后台清理已过期的条目,防止 Map 无限增长
unref()计时器:清理定时器不阻止 Node.js 进程退出
2.4 启动认证 (src/gateway/startup-auth.ts)
Gateway 启动时的认证保障:
- 如果没有配置 token,自动生成安全随机 token 并持久化
- 校验 hooks token 和 gateway token 必须分离(防止密钥复用)
- 解析
SecretRef类型的密码配置 - 确保启动时有有效的认证凭证
安全系统/02-infographic-auth-system-1775150768314.png)
三、设备身份系统
3.1 Ed25519 密钥对管理 (src/infra/device-identity.ts)
每个 OpenClaw 设备(macOS 客户端、iOS 节点、CLI 等)都有一个唯一的加密身份:
typescript
function generateIdentity(): DeviceIdentity {
const { publicKey, privateKey } = crypto.generateKeyPairSync("ed25519");
const publicKeyPem = publicKey.export({ type: "spki", format: "pem" }).toString();
const privateKeyPem = privateKey.export({ type: "pkcs8", format: "pem" }).toString();
const deviceId = fingerprintPublicKey(publicKeyPem); // SHA-256(公钥原始字节)
return { deviceId, publicKeyPem, privateKeyPem };
}为什么选择 Ed25519?
| 特性 | Ed25519 | RSA-2048 |
|---|---|---|
| 密钥长度 | 32 字节 | 256 字节 |
| 签名速度 | 极快 | 较慢 |
| 安全性 | 128 位等效 | 112 位等效 |
| 抗量子 | 否(同 RSA) | 否 |
设备 ID 派生链:
Ed25519 公钥 (PEM)
→ 导出 SPKI DER 格式
→ 去掉 12 字节 SPKI 前缀 → 得到 32 字节原始公钥
→ SHA-256 哈希
→ 十六进制编码 → deviceId文件权限保护: 密钥文件写入时强制 mode: 0o600(仅所有者可读写),写入后再 chmodSync(filePath, 0o600) 双重保障。
3.2 设备认证协议 (src/gateway/device-auth.ts)
设备连接 Gateway 时采用 Challenge-Response 握手:
客户端 Gateway
│ │
├── ws connect + deviceId ──────────────►│
│ │
│◄──────────── challenge nonce ──────────┤
│ │
├── sign(privateKey, nonce+timestamp) ──►│
│ │
│◄──────── verify(publicKey, sig) ───────┤
│ │
│ 连接建立 / 拒绝 │v2/v3 认证载荷包含: deviceId、clientId、scopes(权限范围)、签名时间戳、nonce(防重放)。
3.3 Token 持久化 (src/infra/device-auth-store.ts)
设备认证令牌存储在 ~/.openclaw/device-auth.json,按角色(role)存储/加载/清除:
- 文件权限
0o600 - 支持多角色 token 共存
- 读取时做完整的 JSON 校验和类型检查
安全系统/03-infographic-device-identity-1775150769082.png)
四、SSRF 防护链
4.1 核心原理 (src/infra/net/ssrf.ts)
SSRF(Server-Side Request Forgery)是 Web 应用最常见的高危漏洞之一。攻击者可以利用服务端发起请求的能力,访问内网服务、云实例元数据等。
OpenClaw 的 SSRF 防护是一个多层检查链:
用户提供 URL
│
├─ Layer 1: 协议检查 → 仅允许 http/https
│
├─ Layer 2: 主机名黑名单 → 阻止 localhost、*.local、*.internal、
│ metadata.google.internal 等
│
├─ Layer 3: IP 地址分类 → 阻止私有/保留/特殊用途 IP
│ ├─ IPv4: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16,
│ │ 127.0.0.0/8, 169.254.0.0/16, 0.0.0.0/8 等
│ ├─ IPv6: ::1, fc00::/7, fe80::/10, ::ffff:0:0/96(映射的 IPv4)
│ └─ IPv4-mapped-IPv6 嵌入检测
│
├─ Layer 4: DNS 解析后二次验证 → 防止 DNS Rebinding
│ └─ 解析出的所有 IP 地址都必须通过 Layer 3 检查
│
└─ Layer 5: DNS 钉住(Pinned Lookup)→ 防止 TOCTOU
└─ DNS 查询结果绑定到连接,确保实际连接的 IP 就是检查过的 IP4.2 DNS Rebinding 防护
DNS Rebinding 攻击:攻击者控制一个域名,第一次解析返回公网 IP(通过安全检查),第二次解析返回 127.0.0.1(实际连接时访问内网)。
OpenClaw 的防御:
typescript
// 第一步:解析 DNS
const addresses = await dnsLookup(hostname);
// 第二步:验证所有解析结果
assertAllowedResolvedAddressesOrThrow(addresses, policy);
// 第三步:创建 Pinned Lookup,将验证过的 IP 固定到后续连接
const pinnedLookup = createPinnedLookup({
hostname,
addresses: addresses.map(a => a.address),
});
// 后续 fetch 使用此 pinnedLookup,确保连接的 IP 就是验证过的4.3 特殊 IP 格式防御
攻击者经常用非标准 IP 表示法绕过简单的字符串匹配:
| 绕过技巧 | 示例 | OpenClaw 防御 |
|---|---|---|
| 八进制 IPv4 | 0177.0.0.1 (= 127.0.0.1) | isLegacyIpv4Literal 检测 |
| 十六进制 IPv4 | 0x7f000001 | looksLikeUnsupportedIpv4Literal |
| IPv4-mapped IPv6 | ::ffff:127.0.0.1 | extractEmbeddedIpv4FromIpv6 |
| 畸形 IPv6 | 含有无效格式的地址 | Fail Closed — 解析失败视为私有地址 |
核心原则:Fail Closed — 任何无法正确解析的地址都被视为不安全,宁可误杀不可放过。
4.4 受保护的 HTTP 请求 (src/infra/net/fetch-guard.ts)
为 Agent 工具调用提供安全的 fetch 封装:
strict模式:完整的 SSRF 检查链trusted_env_proxy模式:信任环境代理配置- 自动 DNS 钉住 + 重定向跟踪 + 超时控制
安全系统/04-infographic-ssrf-protection-1775150769890.png)
五、命令执行安全
5.1 三层安全策略 (src/infra/exec-approvals.ts)
Agent 执行 shell 命令是最高风险的操作。OpenClaw 提供三种安全策略:
| 策略 | 行为 | 适用场景 |
|---|---|---|
deny | 拒绝所有命令(默认) | 高安全要求 |
allowlist | 仅允许白名单中的命令 | 生产环境推荐 |
full | 允许所有命令 | 开发/信任环境 |
审批交互模式(ask):
off:不询问,直接按策略执行on-miss:不在白名单中时询问用户(默认)always:每次都询问
5.2 命令白名单匹配 (src/infra/exec-approvals-allowlist.ts)
白名单不是简单的字符串匹配,而是经过完整的命令解析后匹配:
用户命令: "git push origin main"
│
├─ Shell 解析 → ["git", "push", "origin", "main"]
├─ 命令路径解析 → /usr/bin/git
├─ Shell wrapper 解包 → 检测 bash -c "..." 等
├─ 白名单模式匹配 → "git *" 或 "git push *"
└─ 决策: allow/deny5.3 命令混淆检测 (src/infra/exec-obfuscation-detect.ts)
防止攻击者通过编码手段绕过白名单:
| 混淆技巧 | 检测方式 |
|---|---|
echo "Y21k" | base64 -d | sh | 检测 base64 + 管道执行 |
printf "\x63\x6d\x64" | sh | 检测 printf hex + 管道执行 |
eval $(echo "cmd") | 检测 eval + 命令替换 |
$'\x63\x6d\x64' | 检测 ANSI-C 引号编码 |
5.4 安全二进制策略 (src/infra/exec-safe-bin-policy.ts)
对常见命令定义细粒度的参数验证规则:
安全命令 (safe-bin)
├── ls → 允许 -l, -a, -h 等读取标志
├── cat → 允许任意文件路径参数
├── grep → 允许 -r, -n, -i 等搜索标志
├── git → 允许 status, diff, log 等只读子命令
├── node → 限制可执行的脚本路径
└── ...信任路径验证 (src/infra/exec-safe-bin-trust.ts):命令的绝对路径必须位于可信系统目录中(如 /usr/bin、/usr/local/bin),防止 PATH 注入攻击。
5.5 宿主环境变量安全 (src/infra/host-env-security.ts)
Agent 执行命令时,环境变量经过严格过滤:
typescript
function sanitizeHostExecEnv(env: Record<string, string>): Record<string, string> {
// 阻止的危险变量:
// - LD_PRELOAD / LD_LIBRARY_PATH (Linux 动态链接器劫持)
// - DYLD_INSERT_LIBRARIES / DYLD_* (macOS 动态链接器劫持)
// - NODE_OPTIONS (注入 Node.js 启动参数)
// - PATH (防止覆盖系统 PATH)
// ...
}安全系统/05-infographic-exec-security-1775150770641.png)
六、文件系统安全
6.1 路径边界守卫 (src/infra/path-guards.ts)
防止路径遍历攻击(Directory Traversal):
typescript
// isPathInside: 检查 childPath 是否在 parentPath 内部
isPathInside("/home/user/.openclaw/config.json", "/home/user/.openclaw") // true
isPathInside("/etc/passwd", "/home/user/.openclaw") // false
isPathInside("/home/user/.openclaw/../../../etc/passwd", "/home/user/.openclaw") // false6.2 符号链接 & 硬链接防护
| 攻击向量 | 防御文件 | 防御方式 |
|---|---|---|
| 符号链接逃逸 | path-alias-guards.ts | assertNoPathAliasEscape 检测链接目标是否逃出工作区 |
| 硬链接绕过 | hardlink-guards.ts | stat.nlink > 1 检测多链接文件 |
| TOCTOU 竞态 | fs-safe.ts | 多次校验:lstat → open → stat → realpath → stat |
| 原子写入 | fs-safe.ts | 先写临时文件再 rename(原子操作) |
6.3 安全文件操作 (src/infra/fs-safe.ts)
这是整个项目安全设计最精华的文件之一(~500 行):
安全文件打开流程:
lstat(path) ← 检查是否为符号链接
→ open(path, O_NOFOLLOW) ← 禁止跟随符号链接
→ fstat(fd) ← 检查 nlink > 1(硬链接)
→ realpath(path) ← 解析真实路径
→ isPathInside(realpath, root) ← 验证路径在边界内
→ stat(realpath) ← 最终确认
→ 读取/写入平台差异处理:
- Linux: 通过
/proc/self/fd/N访问文件描述符路径 - macOS: 通过
/dev/fd/N访问
安全系统/06-infographic-filesystem-security-1775150771850.png)
七、密钥管理系统
7.1 SecretRef 多源解析 (src/secrets/resolve.ts)
OpenClaw 的密钥不直接存储在配置文件中,而是通过 SecretRef 引用外部来源:
jsonc
{
"channels": {
"telegram": {
"token": {
// SecretRef: 从环境变量读取
"source": "env",
"provider": "default",
"id": "TELEGRAM_BOT_TOKEN"
}
}
}
}三种密钥来源:
| 来源 | 说明 | 安全级别 |
|---|---|---|
env | 环境变量 | 中(依赖进程隔离) |
file | 文件读取 | 高(文件权限控制) |
exec | 命令执行输出 | 高(如 op read 1Password) |
解析安全保障:
typescript
// 文件来源的密钥读取前,会进行路径安全验证:
async function assertSecurePath(params) {
// 1. 必须是绝对路径
// 2. 不能是目录
// 3. 不能是符号链接(除非 allowSymlinkPath)
// 4. 必须在 trustedDirs 内(如果配置了)
// 5. 文件权限不能过于开放(不允许 group/world 可读写)
// 6. 文件所有者必须是当前用户或 root
}资源限制:
- 并发限制:默认 4 个 provider 并发
- 引用限制:每个 provider 最多 512 个 ref
- 批量字节限制:256KB
- 文件/执行超时:5 秒
7.2 配置脱敏 (src/config/redact-snapshot.ts)
导出或展示配置时,自动脱敏敏感字段:
原始配置: { "token": "sk-ant-api03-xxx..." }
↓ redactConfigSnapshot()
脱敏输出: { "token": "<redacted>" }脱敏规则覆盖:password、token、apiKey、appSecret、webhookSecret 等所有已知敏感字段名。
7.3 密钥审计 (src/secrets/audit.ts)
openclaw security audit 会检测:
- 明文密钥(直接写在配置文件中的 API Key)
- 未解析的 SecretRef(指向不存在的环境变量/文件)
- 被遮蔽的引用(配置中的值与环境变量冲突)
- 遗留认证残留(旧版本配置中的密钥痕迹)
安全系统/07-infographic-secret-management-1775150772589.png)
八、DM 私信策略与配对
8.1 DM 策略决策 (src/security/dm-policy-shared.ts)
当外部用户通过消息渠道(Telegram/Discord/WhatsApp 等)发送私信时,OpenClaw 需要决定是否响应:
typescript
export type DmGroupAccessDecision = "allow" | "block" | "pairing";四种 DM 策略:
| 策略 | 行为 | 风险 |
|---|---|---|
disabled | 完全禁用 DM | 最安全 |
pairing | 未知用户需要配对验证(默认) | 安全 |
allowlist | 仅允许白名单中的用户 | 安全 |
open | 允许所有人(危险) | 高风险 |
决策流程(以收到群组消息为例):
收到群组消息
│
├─ [groupPolicy=disabled?] → block (群组策略已禁用)
│
├─ [groupPolicy=allowlist?]
│ ├─ 白名单为空? → block (空白名单)
│ ├─ 发送者在白名单? → allow
│ └─ 发送者不在? → block
│
└─ → allow (其他策略)DM 策略(以收到私信为例):
收到私信
│
├─ [dmPolicy=disabled?] → block
│
├─ [dmPolicy=open?] → allow (危险!)
│
├─ 发送者在白名单? → allow (包括配对存储中的用户)
│
├─ [dmPolicy=pairing?] → 发送配对码,等待管理员审批
│
└─ → block8.2 白名单来源融合
有效白名单 = 配置白名单 ∪ 配对存储白名单:
typescript
const effectiveAllowFrom = mergeDmAllowFromSources({
allowFrom, // 配置文件中的 allowFrom 列表
storeAllowFrom, // 通过配对审批后存储的用户列表
dmPolicy, // 当前 DM 策略
});8.3 命令权限隔离
即使用户被允许发消息,控制命令(如 /restart)有额外的权限检查:
typescript
// 群组命令授权不继承 DM 配对存储的批准
const commandDmAllowFrom = params.isGroup ? configuredAllowFrom : access.effectiveAllowFrom;安全系统/08-infographic-dm-policy-1775150773422.png)
九、安全审计引擎
9.1 审计报告结构 (src/security/audit.ts)
typescript
type SecurityAuditReport = {
ts: number; // 审计时间戳
summary: {
critical: number; // 严重级别数量
warn: number; // 警告级别数量
info: number; // 信息级别数量
};
findings: SecurityAuditFinding[]; // 审计发现列表
deep?: { gateway?: { ... } }; // 深度探测结果
};
type SecurityAuditFinding = {
checkId: string; // 如 "fs.state_dir.perms_world_writable"
severity: "info" | "warn" | "critical";
title: string; // 人类可读标题
detail: string; // 详细描述
remediation?: string; // 修复建议
};9.2 审计检查项分类
文件系统审计 (audit-fs.ts):
- 状态目录(
~/.openclaw)权限检查 - 配置文件权限检查
- 符号链接检测
- World-writable / Group-writable 检测
- Windows ACL 检查(
icacls)
渠道安全审计 (audit-channel.ts):
- 各渠道的 DM 策略配置检查
- Webhook 安全配置
- Bot Token 暴露风险
- allowFrom 配置完整性
同步审计 (audit-extra.sync.ts):
- 危险配置标志(
dangerously*前缀) - exec 安全策略检查
- 文件系统工作区限制
- 沙箱配置验证
- 插件信任级别
- 模型安全卫生检查
异步审计 (audit-extra.async.ts):
- 网关 HTTP 探测(
--deep模式) - 活跃连接检查
- 远程配置验证
- 安全码(code safety)扫描
9.3 CLI 命令
bash
# 基础审计
openclaw security audit
# 深度审计(包含运行时探测)
openclaw security audit --deep
# 输出 JSON 格式
openclaw security audit --json
# 自动修复可修复的问题
openclaw security audit --fix9.4 自动修复引擎 (src/security/fix.ts)
--fix 模式可以自动修复:
- 文件权限过于开放 → 收紧到
0o600/0o700 - 配置中的明文密钥 → 建议迁移到 SecretRef
- 缺少关键安全配置 → 添加安全默认值
安全系统/09-infographic-audit-engine-1775150774177.png)
十、安全正则与 ReDoS 防护
10.1 ReDoS 攻击原理
正则表达式拒绝服务(ReDoS):精心构造的输入可以让某些正则表达式的匹配时间呈指数级增长。例如:
正则: /^(a+)+$/
输入: "aaaaaaaaaaaaaaaaab"
→ 匹配时间:O(2^n),n = 'a' 的个数10.2 安全正则验证 (src/security/safe-regex.ts)
OpenClaw 实现了一个自定义的正则表达式安全分析器(~190 行),用于检测用户配置中可能导致 ReDoS 的正则:
正则源码
→ tokenizePattern() ← 词法分析
→ analyzeTokensForNestedRepetition() ← 检测嵌套量词
├─ (a+)+ → 危险!嵌套重复
├─ (a|b)* → 安全(交替分支长度一致)
└─ (a|ab)* → 危险!交替分支长度不一致 + 重复
→ 缓存结果(LRU, 256 条上限)
→ 运行时测试窗口:2048 字符检测的核心模式: 嵌套量词(quantifier applied to token that already contains repetition)。这是 ReDoS 的根本原因。
安全系统/10-infographic-redos-protection-1775150775024.png)
十一、沙箱安全验证
11.1 Docker 沙箱配置检查 (src/agents/sandbox/validate-sandbox-security.ts)
当配置 Docker 沙箱运行 Agent 时,验证 Docker 配置是否安全:
| 检查项 | 阻止的危险配置 | 原因 |
|---|---|---|
| 敏感主机路径挂载 | -v /:/host | 容器可访问整个宿主文件系统 |
| Seccomp 未限制 | --security-opt seccomp=unconfined | 容器可执行所有系统调用 |
| AppArmor 未限制 | --security-opt apparmor=unconfined | 绕过强制访问控制 |
| 危险网络模式 | --network host | 容器共享宿主网络栈 |
| 特权模式 | --privileged | 容器获得几乎全部宿主权限 |
11.2 浏览器安全 (src/browser/)
- 导航守卫 (
navigation-guard.ts):浏览器工具访问 URL 前,复用 SSRF 防护链验证目标安全性 - 控制面板认证 (
control-auth.ts):浏览器控制面板复用 Gateway auth token,防止未授权访问
安全系统/11-infographic-sandbox-security-1775150775919.png)
十二、模块关系与设计总结
12.1 安全模块依赖图
┌─────────────────────────────────┐
│ openclaw security audit │
│ (CLI 命令入口) │
└───────────────┬─────────────────┘
│
┌───────────────▼─────────────────┐
│ audit.ts (审计编排) │
└──┬────┬────┬────┬───────────────┘
│ │ │ │
┌────────────▼┐ ┌▼──┐ ┌▼──┐ ┌▼──────────────┐
│audit-channel│ │fs │ │sync│ │ async │
│ (渠道审计) │ │ │ │ │ │(运行时探测) │
└──────┬──────┘ └───┘ └─┬──┘ └───────────────┘
│ │
┌────────────▼────────────────▼─────────────────────┐
│ 被审计的安全子系统 │
├────────────┬────────────┬────────────┬─────────────┤
│ Gateway │ 设备身份 │ SSRF 防护 │ 命令执行 │
│ Auth │ Ed25519 │ DNS 钉住 │ 白名单 │
├────────────┼────────────┼────────────┼─────────────┤
│ DM 策略 │ 文件系统 │ 密钥管理 │ 沙箱验证 │
│ 配对 │ 路径守卫 │ SecretRef │ Docker │
└────────────┴────────────┴────────────┴─────────────┘
│
┌─────────▼──────────────┐
│ secret-equal.ts │
│ (时序安全比较 — 基石) │
└─────────────────────────┘12.2 关键设计模式
| 模式 | 应用 | 说明 |
|---|---|---|
| Fail Closed | SSRF IP 解析 | 解析失败 → 视为不安全 |
| 深度防御 | 文件操作、命令执行 | 多层检查叠加 |
| 最小权限 | DM 策略默认 pairing | 默认不信任 |
| 时序安全 | safeEqualSecret | 恒定时间比较 |
| 依赖注入 | 审计引擎 | 所有外部依赖可测试替换 |
| 滑动窗口 | 频率限制 | 精确的时间窗口管理 |
| 原子操作 | 文件写入 | tmp → rename |
| 资源限制 | 密钥解析 | 并发/大小/超时控制 |
12.3 安全文件清单(按阅读优先级)
| 优先级 | 文件 | 行数 | 难度 |
|---|---|---|---|
| 🔴 必读 | security/secret-equal.ts | ~12 | ★☆☆☆☆ |
| 🔴 必读 | gateway/auth-rate-limit.ts | ~232 | ★★★☆☆ |
| 🔴 必读 | infra/device-identity.ts | ~182 | ★★★☆☆ |
| 🟡 推荐 | security/dm-policy-shared.ts | ~320 | ★★★☆☆ |
| 🟡 推荐 | infra/net/ssrf.ts | ~363 | ★★★★☆ |
| 🟡 推荐 | gateway/auth.ts | ~487 | ★★★★☆ |
| 🟡 推荐 | infra/exec-approvals.ts | ~420 | ★★★★☆ |
| 🟢 选读 | security/audit.ts | ~690 | ★★★★☆ |
| 🟢 选读 | secrets/resolve.ts | ~350 | ★★★★☆ |
| 🟢 选读 | security/safe-regex.ts | ~190 | ★★★★★ |
| 🟢 选读 | infra/exec-obfuscation-detect.ts | ~130 | ★★★☆☆ |
| 🟢 选读 | infra/fs-safe.ts | ~500 | ★★★★★ |
12.4 思考题
为什么
safeEqualSecret先做 SHA-256 哈希再比较,而不是直接用timingSafeEqual?提示:考虑两个字符串长度不等的情况。
SSRF 防护中,为什么要做 DNS 钉住(Pinned Lookup)?仅做 DNS 解析后检查 IP 不够吗?
提示:考虑 DNS 解析和实际 TCP 连接之间的时间窗口。
命令执行审批系统为什么需要混淆检测?白名单匹配不是已经够安全了吗?
提示:考虑
bash -c "$(echo 'cm0gLXJmIC8=' | base64 -d)"这样的命令。DM 配对策略中,群组命令授权为什么不继承配对存储的批准?
提示:考虑配对是针对 DM 个人的信任,群组是不同的安全边界。
安全系统/12-infographic-design-patterns-1775150776731.png)
📌 小结: OpenClaw 的安全系统体现了纵深防御的理念——从网络层(SSRF 防护、TLS 强制)到应用层(认证授权、命令审批)再到数据层(密钥管理、配置脱敏),每一层都有独立的安全机制。这种设计确保了即使某一层被突破,攻击者仍然面对多重防线。阅读这些安全代码不仅能理解 OpenClaw 的工程实践,更能学习到通用的安全开发方法论。