openclaw-weixin插件深度分析报告
一、概览
openclaw-weixin 是 OpenClaw 框架的官方微信(Weixin)渠道插件,作用是将微信账号接入 OpenClaw 的 AI Agent 管道——让 AI 可以直接通过微信接收和发送消息。
插件采用 扫码登录 + 长轮询接收消息 的架构,这与市面上大量微信机器人方案的底层逻辑一致,但有一点不同:它走的是 Tencent 官方提供的 ilinkai.weixin.qq.com 接口,而非 PC 客户端协议或网页微信协议逆向。
技术栈:TypeScript / Node.js 22+,依赖极其精简(仅 qrcode-terminal + zod)
二、架构分析
2.1 目录结构
|
|
2.2 消息流向
入站(接收消息)
|
|
出站(发送消息)
|
|
2.3 服务端接口汇总
所有请求走 HTTPS,Content-Type: application/json,携带 Authorization: Bearer <token>。
| 接口路径 | 用途 |
|---|---|
ilink/bot/getupdates |
长轮询拉取新消息 |
ilink/bot/sendmessage |
发送文字/媒体消息 |
ilink/bot/getuploadurl |
获取 CDN 预签名上传地址 |
ilink/bot/getconfig |
获取 typing ticket(24h 缓存) |
ilink/bot/sendtyping |
发送"正在输入"状态 |
ilink/bot/get_bot_qrcode |
获取扫码登录二维码 |
ilink/bot/get_qrcode_status |
轮询扫码状态 |
CDN 默认地址:https://novac2c.cdn.weixin.qq.com/c2c
2.4 账号认证流程
|
|
QR 码 TTL:5 分钟,过期自动刷新,最多刷新 3 次。
三、安全分析
3.1 凭证存储
文件路径:~/.openclaw/openclaw-weixin/accounts/{accountId}.json
内容结构:
|
|
权限设置(accounts.ts:172):
|
|
文件权限做了 0o600,但有一个细节缺陷——权限设置包裹在 try/catch 里且静默忽略失败,如果运行环境不支持 chmod(如某些容器、Windows WSL),凭证文件将以默认权限暴露。
3.2 AES-128-ECB 加密模式
文件:src/cdn/aes-ecb.ts
|
|
ECB(电子密码本)模式是一种确定性加密:相同明文 + 相同密钥 → 相同密文。这是密码学中的经典反例(可进行频率分析和块替换攻击)。
不过这里有一个缓解因素:每个文件使用独立随机生成的 16 字节 AES Key(crypto.randomBytes(16)),因此不同文件无法交叉关联。
结论:ECB 模式很可能是 Weixin CDN 协议层面的要求,而非插件设计缺陷。密钥随机化在一定程度上补救了 ECB 的最大缺陷,但仍不如 CBC/GCM 安全。
3.3 Context Token 内存存储
文件:src/messaging/inbound.ts:15
|
|
Context Token 是回复消息的"通行证",由服务端在每条入站消息中下发,插件用 accountId:userId 作 key 存在进程内存里。
问题:
- 进程重启后 token 全部丢失
- Agent 回复若跨越重启,将因缺少 token 发送失败
- 进程内存 dump 或
/proc/[pid]/mem读取可提取 token
3.4 日志中的敏感信息
文件:src/util/redact.ts
|
|
脱敏仅是截断前 200 字符,并非字段级脱敏。若 JSON body 中 context_token 出现在前 200 字符内,会以明文写入日志。
日志路径:/tmp/openclaw/openclaw-YYYY-MM-DD.log
额外问题:/tmp 目录在大多数 Unix 系统下全局可读,日志文件默认权限也没有限制,任意有 shell 权限的用户都能读取。
3.5 Session 暂停逻辑的遗漏
文件:src/api/session-guard.ts
当服务端返回 errcode -14(会话过期),插件会将该账号标记为"暂停"状态持续 1 小时。但是,长轮询监控循环(monitor.ts)并不检查 session 是否暂停,会继续调用
getUpdates(),可能持续触发无效请求或导致状态不一致。
3.6 无证书 Pinning
标准 Node.js fetch() 不做证书 Pinning。在证书链遭受 MITM 攻击(如企业代理、恶意 CA 注入)的场景下,HTTPS 流量可被解密,bot token 可被截获。
3.7 安全评级汇总
| 问题 | 严重程度 | 文件 |
|---|---|---|
| Context Token 仅存内存,重启丢失 | 高 | inbound.ts:15 |
| AES-ECB 加密模式 | 中(协议约束) | aes-ecb.ts:7 |
| 日志含部分 context_token 明文 | 中 | redact.ts, api.ts:103 |
| Session 暂停未覆盖监控循环 | 中 | session-guard.ts, monitor.ts |
| 凭证 chmod 失败静默忽略 | 低 | accounts.ts:172 |
| 日志文件全局可读(/tmp) | 低 | logger.ts:106 |
| 无证书 Pinning | 低 | api.ts:108 |
| 无 API 响应 Schema 校验 | 低 | api.ts:150 |
四、隐私分析
4.1 收集哪些数据
插件在本地会存储以下数据:
| 数据 | 路径 | 说明 |
|---|---|---|
| Bot Token | ~/.openclaw/openclaw-weixin/accounts/*.json |
永久存储,权限 0o600 |
| 账号 UserId | 同上 | 扫码确认的微信号标识 |
| 同步游标 | ~/.openclaw/openclaw-weixin/accounts/*.sync.json |
断点续传用,不含消息内容 |
| 日志 | /tmp/openclaw/openclaw-YYYY-MM-DD.log |
含用户 ID、部分消息片段(前 200 字符) |
| 入站媒体文件 | ~/.openclaw/weixin/media/inbound/ 或系统 tmp |
解密后明文存储,清理策略由框架决定 |
4.2 数据不出本地
插件没有内置遥测或追踪,所有 API 调用仅面向 ilinkai.weixin.qq.com,不向其他第三方发送数据。
唯一的数据上传入口是 CLI 命令(完全可选、需用户主动触发):
|
|
该命令将本地日志文件以 multipart/form-data 形式 POST 到用户指定的 URL,上传地址由用户控制,不是预设的 Anthropic/OpenClaw 服务器。
4.3 端到端加密现状
- 传输层:全程 HTTPS,合格
- CDN 存储:AES-128 加密,但密钥随消息下发(非端到端)
- 本地存储:解密后明文写入临时文件,无额外加密
- Agent 处理:消息以明文传入 AI Agent
结论:这不是端到端加密方案,消息在服务端(Weixin ilink 服务)和 OpenClaw Agent 处均可以明文访问。
4.4 用户隔离
默认情况下,所有联系你 bot 的微信用户共享同一个 AI 对话上下文。
如需隔离,需要显式配置:
|
|
开启后,每个微信用户获得独立的 Agent 会话,数据不互通。
4.5 微信账号与 OpenClaw 账号的关联
src/auth/pairing.ts 在登录时将扫码者的 ilink_user_id 写入 OpenClaw 的 allowFrom 列表,后续只有该用户才能向 bot 下达指令。这是一个合理的授权控制设计,但意味着微信 ID 会被持久化关联到 OpenClaw 账号体系中。
五、与 OpenClaw 的集成分析
5.1 插件注册机制
插件通过 openclaw.plugin.json 声明身份:
|
|
index.ts 向 OpenClaw 框架注册三个组件:
- Channel Plugin:处理消息收发
- CLI 子命令:
openclaw openclaw-weixin logs-upload - Runtime Context:框架 API 的引用
5.2 Channel Plugin 能力声明
|
|
5.3 使用的 OpenClaw SDK API
|
|
5.4 Agent Prompt 注入
插件向 AI Agent 的系统 Prompt 注入以下提示(channel.ts:105):
- 如何通过
message工具发送图片(支持本地路径和 HTTPS URL) - 图片搜索后直接发图的正确姿势
- 必须使用绝对路径(微信场景没有 cwd 上下文)
- 创建定时任务时
delivery.to必须设置为微信 ID
这是一种典型的 Channel-specific prompt engineering,让通用 Agent 懂得微信渠道的发送规范。
5.5 媒体发送流程详解
|
|
密钥随消息发送,接收方(或服务端)负责解密。这套流程是 Weixin ilink 协议层面的设计,插件按协议实现。
六、横向对比:它和其他微信机器人方案有什么不同?
| 维度 | openclaw-weixin | 传统 PC 协议逆向(如 Wechaty IPAD) | 网页微信 |
|---|---|---|---|
| 合规性 | 官方 ilink 接口 | 逆向非公开协议,存在封号风险 | 官方已停止支持 |
| 稳定性 | 依赖 ilinkai 服务可用性 | 协议改版即失效 | 极不稳定 |
| 功能 | 私聊,图片/视频/文件/语音 | 群聊、更多消息类型 | 极为受限 |
| 认证方式 | 扫码 + server token | 扫码 + 内存 session | Cookie |
| 媒体加密 | AES-128-ECB,有一定保障 | 通常明文或用内置密钥 | 明文 |
| Agent 集成 | OpenClaw 原生深度集成 | 需要额外适配层 | 无 |
七、潜在风险场景
场景一:服务器被攻破
攻击者获得 ~/.openclaw/openclaw-weixin/accounts/ 目录访问权:
- 拿到 bot token → 可冒充 bot 发送消息给任意联系人
- 没有 token 轮换机制,需要手动重新登录才能失效旧 token
场景二:进程崩溃后 Context Token 丢失
用户发了一条消息,AI 正在生成回复时进程崩溃:
- Context Token 丢失,重启后无法回复
- 用户端看起来消息"发出去了"但没有回应
- 需要用户再发一条消息才能恢复
场景三:日志泄露
/tmp/openclaw/*.log 被其他本地用户读取(共享服务器场景):
- 用户 ID 暴露
- 消息内容片段(前 200 字符)暴露
- 若 context_token 出现在请求 body 前段,可被提取用于消息伪造
场景四:QR 码时序攻击
Bot 登录时终端展示二维码:
- 若终端输出被捕获(如远程日志、截屏),攻击者可抢先扫码
- 扫码 TTL 5 分钟内有效,超时会刷新
- 扫码本身由用户微信账号的推送确认,纯窃取 QR 图片不能完成登录
八、改进建议
高优先级
- Context Token 持久化:写入磁盘(带 TTL 清理),避免重启后丢失回复能力
- Session 暂停检查:监控循环入口增加
assertSessionActive()调用 - 日志目录权限限制:
/tmp/openclaw设为 0o700,日志文件设为 0o600 - context_token 字段级脱敏:在
redactBody()中对 JSON key 进行专项屏蔽
中优先级
- API 响应 Schema 校验:利用已有的
zod依赖对所有服务端响应做校验 - chmod 失败告警:捕获异常后写日志,不能静默丢弃
- QR 码轮询退避:由固定 1 秒改为指数退避,减少无效请求
长期方向
- Token 轮换机制:支持定期或手动触发 token 刷新
- 媒体文件临时目录权限:将解密后的媒体文件写入权限受控的目录,而非系统
/tmp - WebSocket 支持:当 ilink 协议支持时,迁移为 WebSocket 实时推送,减少长轮询开销
九、总结
openclaw-weixin 是一个架构清晰、代码质量较高的 OpenClaw 渠道插件。它走的是 Tencent 官方 ilink API,没有逆向非公开协议,合规层面比市面上大多数微信机器人方案更稳健。
主要亮点:
- 插件与 OpenClaw 框架深度集成,开箱即用
- 媒体文件通过 CDN + AES 加密传输,基本符合安全规范
- 无内置遥测,数据不出本地
- 代码模块化良好,依赖极简
主要隐患:
- Context Token 仅存内存,高可用场景有风险
- 日志权限管理粗放,共享服务器下有信息泄露风险
- 没有 API 响应的运行时类型校验
- 凭证 chmod 失败被静默忽略
对于个人或小规模部署,现有实现已经足够;若要在多用户、生产级环境使用,建议优先处理上述高优先级问题。
附录:关键文件速查
| 文件 | 关键行 | 内容 |
|---|---|---|
index.ts |
1-27 | 插件注册入口 |
src/channel.ts |
77-380 | Channel Plugin 核心实现 |
src/auth/login-qr.ts |
126-333 | 扫码登录完整流程 |
src/auth/accounts.ts |
140-180 | 凭证读写与权限设置 |
src/api/api.ts |
40-160 | HTTP 客户端封装 |
src/monitor/monitor.ts |
37-166 | 长轮询主循环 |
src/cdn/aes-ecb.ts |
1-21 | AES-128-ECB 加解密 |
src/cdn/upload.ts |
52-100 | 媒体上传流程 |
src/messaging/inbound.ts |
15-26 | Context Token 内存存储 |
src/util/logger.ts |
100-115 | 日志写入路径与权限 |
src/util/redact.ts |
28-31 | 日志脱敏(截断 200 字符) |
src/api/session-guard.ts |
1-58 | Session 暂停/恢复逻辑 |
本报告基于静态代码审计,未进行动态运行测试。如有任何问题欢迎讨论。