VisionClaw 安全实践:访问码体系、HMAC 签名 State 与 OAuth 凭据保险库设计
【免费下载链接】VisionClawReal-time AI assistant for Meta Ray-Ban smart glasses -- voice + vision + agentic actions via Gemini Live and OpenClaw项目地址: https://gitcode.com/gh_mirrors/vi/VisionClaw
VisionClaw 是一款运行在 Meta Ray-Ban 智能眼镜上的实时 AI 助手,靠语音 + 视觉 + 代理动作完成任务。正因为它能读你的日历、代发邮件、操作第三方应用,VisionClaw 安全设计成为整个系统的底座:网关层用访问码把守大门,用 HMAC 签名的 state 参数防伪造回调,把 OAuth 凭据统一锁进按用户隔离的保险库。本文带你从新手视角看懂这三道防线是怎么落地的。
上图给出了整体分层:眼镜端交给 Gemini Live 做实时感知,动作类任务委托给网关(OpenClaw)。所有敏感凭据都留在网关一侧,App 只拿到"够用的钥匙"——这正是下面三层防护的前提。
访问码体系:第一道门禁如何工作 🚪
首次启动 App,你会看到一屏"请输入访问码"。这里的访问码(access code)不是作者手动发放的口令,而是 App 所指向的那个网关签发的令牌:
- 自托管网关在
.env里设置GATEWAY_TOKENS="<你的密钥>:<你的用户名>",这个密钥就是你的访问码,解析逻辑见 gateway/src/config.ts。 - App 端提交前会先向网关验证,验证通过才解锁,打错码会立刻提示"未被识别",见 AccessCodeScreen.kt。
- 现在的主入口已升级为 Google 登录:访问码路径降级为"使用自建网关"的备选项,两条路径共用同一套令牌校验。
账号状态机:pending / approved / revoked 三级审批
网关给每个自注册账号维护一个状态字段,访问控制完全由它驱动:
| 状态 | 行为 |
|---|---|
pending | 持有令牌,但除/me外所有接口视同无效令牌 |
approved | 正常放行(白名单邮箱域可自动批准) |
revoked | 全部拒绝,凭据不再发放 |
审批接口仅限"服务令牌"调用,普通用户无法枚举他人账号,见 gateway/src/auth.ts。
令牌只存哈希,最多保留 5 个
每次登录签发的 bearer token 形如vc-+ 16 字节随机数;服务器只保存它的 SHA-256 哈希,原始令牌仅存在于设备端,且每个账号最多保留最近 5 个哈希——泄露存储文件也反推不出可用令牌,实现见 gateway/src/auth.ts。
HMAC 签名 State:如何让 OAuth 回调不可伪造 🔏
任何 OAuth 流程都绕不开state参数,VisionClaw 的做法在 gateway/src/auth.ts 与 gateway/src/connect.ts 中高度一致,核心是四步:
- 载荷签名:把
{nonce, userId, appId, ts}做 base64url 编码,再用HMAC-SHA256计算 16 字节截断的 MAC,拼成body.mac两段式结构。 - 恒定时间比较:验证时用
timingSafeEqual对比 MAC,杜绝时序侧信道攻击。 - 10 分钟 TTL:state 内嵌时间戳,过期即作废,防止链接被长期截留。
- 签名密钥兜底:未配置
STATE_SECRET时自动退化为进程内随机密钥——重启会让进行中的登录失效,但保证了无配置环境下也不存在"无签名 state"攻击面(见 auth.ts 第 33-35 行)。
一次性 Nonce 停车机制:令牌绝不经过 URL 🚗
登录完成后,令牌并不走深链接回传(那样会留在浏览器历史里),而是"停"在网关内存中:
- App 生成一个 16 字节随机 nonce,打开浏览器登录;
- 回调成功时,网关把新令牌park到该 nonce 下;
- App 端每 2 秒轮询
/auth/exchange取回令牌,取走即删、二次请求返回 410(见 auth.ts 第 308-337 行); - 轮询接口还叠加了"每 IP 每分钟 60 次"的速率限制,防暴力枚举 nonce。
连接第三方应用(如 Notion、Slack)时,同样机制与PKCE(S256)结合:verifier 只活到回调那一刻,以签名 state 为键存取,见 connect.ts 第 372-385 行。
OAuth 凭据保险库:每个用户一个独立金库 💰
保险库按用户隔离,凭据按 MCP 地址索引
App 连接日历、Gmail、Notion、Slack 时,网关换取的 refresh token 并不落在本地 JSON 文件里,而是写入 Anthropic Cloud 提供的Vault(凭据保险库):
- 首次使用即为该用户创建一个专属 vault(
visionclaw-vault-<userId>),见 gateway/src/provision.ts; - 每条凭据以 MCP 服务器 URL 为键,重连即替换,避免同服务器累积多份过期凭据,见 gateway/src/connect.ts;
- 后续 token 过期由 Anthropic 侧自动用保险库里的 refresh 配置续期,App 与网关都不持有长期刷新逻辑。
最小权限的 Scope 设计 🎯
申请权限严格遵循"够用就好",例如 gateway/src/apps.ts 中 Gmail 只申请gmail.readonly+gmail.send(汇总与代发邮件),刻意不申请修改标签、删除邮件的权限;日历同样只开放事件读写而非管理权限。
敏感配置永不出现在代码库
- Google/Slack 的 client_id、client_secret 全部来自环境变量,未配置时对应应用自动隐藏(
appAvailable检查); - LiveKit 的 API secret 只在网关进程内签名 15 分钟有效期的房间 JWT,App 拿到的只是短票,见 gateway/src/server.ts;
- 本地 store 文件采用"先写临时文件再原子 rename"的方式落盘,避免半写状态,见 gateway/src/store.ts。
安全实践清单:部署自己的 VisionClaw 网关 📋
如果你想自托管一套网关,以下配置项与防护一一对应(完整说明见 gateway/README.md):
| 配置项 | 防护目标 |
|---|---|
GATEWAY_TOKENS | 访问码体系,未设置时启动即告警 |
GATEWAY_SERVICE_TOKEN | 管理员/worker 专用令牌,同时兼任 state 签名密钥 |
STATE_SECRET | 登录/连接流程的 state 签名密钥,显式配置可跨重启保活 |
REGISTRATION_OPEN=false | 注册总开关(kill switch),随时关闭新账号 |
AUTO_APPROVE_DOMAINS | 仅放行白名单邮箱域,其余进入人工审批队列 |
给新手的 3 条要点:
- 访问码是"指向哪个网关、哪个网关说了算",自部署时它就是你的
.env里那串密钥; - 任何 OAuth 回调都先验 state 再换码,state 被篡改或过期都会直接 400;
- 凭据集中放在按用户隔离的保险库里,代码库与本地文件里找不到可用的 refresh token。
这套"访问码门禁 + 签名 state + 保险库凭据"的组合,让 VisionClaw 在替你动日历、发邮件的同时,把每把钥匙都锁在了对的地方 🔐。
【免费下载链接】VisionClawReal-time AI assistant for Meta Ray-Ban smart glasses -- voice + vision + agentic actions via Gemini Live and OpenClaw项目地址: https://gitcode.com/gh_mirrors/vi/VisionClaw
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考