1. 从 ClawHub 事件说起:Skill 供应链为什么突然成了高危区
ClawHub 是 OpenClaw 生态里默认的 Skill 分发入口,你可以把它理解成 AI Agent 世界的应用商店。开发者写好的 Skill 上传上去,用户一条命令就能装进自己的 workspace,Agent 立刻多出一项能力。方便是真方便,但这次 Silverfort 披露的攻击把问题摊开了:攻击者利用平台排名机制的漏洞,人为刷高一个伪装成「Outlook Graph Integration」的恶意 Skill 的下载量,让它冲到搜索结果第一。六天时间,全球 50 个城市执行了 3900 次,用户名和域名信息被静默外传。
这件事最扎心的地方不是漏洞本身,而是受害者的心态。装这个 Skill 的人不是不懂安全,他们只是看到「排名第一」就默认它可信。社交证明在软件分发里一直是把双刃剑,到了 AI Agent 场景,这把刀更锋利了——因为 Skill 拿到的权限往往比传统插件大得多,它能读文件、能发网络请求、能调用你配置好的模型 API。
我试过在本地 workspace 里翻一个第三方 Skill 的源码,表面上是帮你整理日历,实际上它在初始化阶段就读取了环境变量里的 API Key,然后往一个不在文档里出现的域名发了一次 POST。这种「安静地多做一点事」的模式,正是供应链攻击最舒服的藏身之处。
所以问题不是「要不要用 Skill」,而是「怎么让 Skill 拿不到它不该拿的东西」。这篇就围绕这个目标,给你一套可复制的配置骨架:用 TaoToken 统一 Key 和 API 通道,把凭证暴露面从「每个 Skill 各自持有」收敛到「一个受控入口」,再配合 OpenClaw 的 config.toml 和 settings.json 做权限隔离。
2. 前置准备:TaoToken 统一 Key 与 OpenClaw 环境对齐
在动手改配置之前,先把两件事理清楚:TaoToken 在这套方案里扮演什么角色,以及 OpenClaw 的配置文件各自管什么。
TaoToken 的核心价值是「统一入口」。你不需要给每个 Skill 单独发一把模型 API Key,也不需要让 Skill 直接持有你的原始凭证。所有模型调用走 TaoToken 的 API 通道,Key 只在 OpenClaw 主进程的配置里出现一次,Skill 通过内部接口间接使用。这样即使某个 Skill 被投毒,它能拿到的也只是「一次被审计过的调用」,而不是一把可以无限复用的钥匙。
你需要先拿到自己的 API Key。访问 https://taotoken.net/api-keys 创建,注意这个页面是控制台的一部分,创建后 Key 只显示一次,复制到安全的地方。如果你还没注册,官网入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,注册流程不复杂,这里不展开。
OpenClaw 侧需要确认版本。Skill 供应链相关的权限控制字段在较新的版本里才完整,建议先跑一次版本检查:
openclaw --version如果低于 0.9.x,建议先升级。升级命令取决于你的安装方式,npm 全局安装的话:
npm install -g @openclaw/cli@latest环境变量层面,我建议把 TaoToken 的 Key 放在系统级环境变量里,而不是写死在项目文件中。Linux/macOS 下编辑~/.zshrc或~/.bashrc:
export TAOTOKEN_API_KEY="sk-你的实际Key" export TAOTOKEN_BASE_URL="https://taotoken.net/api"Windows 用 PowerShell 的话:
[System.Environment]::SetEnvironmentVariable("TAOTOKEN_API_KEY","sk-你的实际Key","User") [System.Environment]::SetEnvironmentVariable("TAOTOKEN_BASE_URL","https://taotoken.net/api","User")设置完记得重开终端,或者source ~/.zshrc让变量生效。验证一下:
echo $TAOTOKEN_API_KEY能打印出 Key 的前几位就说明环境变量到位了。这一步看起来基础,但后面所有配置都依赖它,别跳过。
3. 可复制配置:config.toml 与 settings.json 骨架
OpenClaw 的配置分两层:config.toml管运行时行为,settings.json管 Skill 权限和模型通道。下面这份骨架你可以直接抄,改掉注释里标出的占位符即可。
先看config.toml,放在~/.openclaw/config.toml:
# OpenClaw 主配置 [agent] name = "my-openclaw" workspace = "~/.openclaw/workspace" # 模型通道统一走 TaoToken [model] provider = "taotoken" base_url = "https://taotoken.net/api" api_key_env = "TAOTOKEN_API_KEY" # 从环境变量读取,不写明文 default_model = "claude-sonnet-4-20250514" timeout_seconds = 60 max_retries = 2 # Skill 供应链安全策略 [skill.security] # 禁止 Skill 直接读取模型 Key allow_direct_key_access = false # 所有 Skill 的模型调用必须经过主进程代理 proxy_model_calls = true # 未在 settings.json 白名单里的 Skill 默认拒绝 default_policy = "deny" # 记录每次 Skill 的模型调用,便于审计 audit_log = "~/.openclaw/logs/skill-audit.log" # 网络出口限制 [skill.network] # 只允许 Skill 访问白名单域名 enforce_allowlist = true allowlist = [ "taotoken.net", "api.taotoken.net" ]这份配置的关键点有三个。第一,api_key_env让 OpenClaw 从环境变量读 Key,配置文件本身不含敏感信息,就算你不小心把 config.toml 提交到 Git 也不会泄露。第二,allow_direct_key_access = false配合proxy_model_calls = true,把 Skill 和 Key 物理隔开,Skill 只能请求主进程代为调用。第三,enforce_allowlist把 Skill 的网络出口锁死在 TaoToken 域名上,恶意 Skill 想往外部服务器发数据也发不出去。
再看settings.json,放在~/.openclaw/workspace/settings.json:
{ "skills": { "installed": [ { "name": "outlook-graph-integration", "source": "clawhub", "trust_level": "reviewed", "allowed_models": ["claude-sonnet-4-20250514"], "max_calls_per_hour": 30, "network_access": false }, { "name": "local-file-organizer", "source": "local", "trust_level": "trusted", "allowed_models": ["claude-sonnet-4-20250514"], "max_calls_per_hour": 100, "network_access": false } ], "policy": { "unknown_skill": "deny", "require_review_on_install": true, "auto_update": false } }, "model_channel": { "provider": "taotoken", "endpoint": "https://taotoken.net/api", "key_ref": "env:TAOTOKEN_API_KEY" } }settings.json里每个 Skill 都有独立的权限条目。trust_level分三档:trusted是本地自己写的,reviewed是经过人工审查的第三方 Skill,untrusted默认拒绝。network_access: false意味着这个 Skill 不能自己发网络请求,所有模型调用走主进程代理。max_calls_per_hour是速率限制,防止某个 Skill 被投毒后疯狂调用消耗你的额度。
auto_update: false这一条值得单独说。ClawHub 事件里,很多风险来自 Skill 的静默更新——今天安全的版本,明天推一个新版就可能引入恶意逻辑。关掉自动更新,每次更新前手动审查,虽然麻烦一点,但供应链安全本来就是用一点便利换可控性。
配置写完后,跑一次校验:
openclaw config validate输出Configuration valid就说明格式没问题。如果有报错,通常是 TOML 的缩进或 JSON 的逗号问题,按提示行号改就行。
4. 验证请求:跑通一次受控的 Skill 调用
配置到位后,需要实际验证一次 Skill 调用是否走了 TaoToken 通道,以及权限限制是否生效。这里用一个最小化的测试 Skill 来演示。
先创建一个测试用的 Skill 目录:
mkdir -p ~/.openclaw/workspace/skills/test-echo cd ~/.openclaw/workspace/skills/test-echo写一个最简单的 Skill 定义skill.json:
{ "name": "test-echo", "version": "1.0.0", "description": "最小化测试 Skill,验证模型通道与权限控制", "entry": "index.js", "permissions": { "model_access": true, "network_access": false, "file_access": false } }再写index.js:
module.exports = async function handler(input, context) { // 通过主进程代理调用模型,不直接持有 Key const response = await context.model.chat({ model: "claude-sonnet-4-20250514", messages: [ { role: "user", content: `请原样返回这句话:${input.text}` } ] }); return { echo: response.content }; };注意context.model.chat这个调用方式。Skill 本身不 import 任何 HTTP 客户端,也不读环境变量,它只是把请求交给 OpenClaw 主进程,主进程再用 TaoToken 的 Key 去调 API。这就是「统一 Key」的实际落地方式。
在settings.json的installed数组里加上这个测试 Skill:
{ "name": "test-echo", "source": "local", "trust_level": "trusted", "allowed_models": ["claude-sonnet-4-20250514"], "max_calls_per_hour": 10, "network_access": false }然后通过 OpenClaw CLI 触发一次调用:
openclaw skill run test-echo --input '{"text":"供应链安全测试"}'如果一切正常,你会看到类似这样的输出:
{ "echo": "供应链安全测试", "model_used": "claude-sonnet-4-20250514", "channel": "taotoken", "latency_ms": 842 }channel字段显示taotoken,说明请求确实走了统一通道。同时去检查审计日志:
tail -n 5 ~/.openclaw/logs/skill-audit.log应该能看到一条记录,包含 Skill 名称、调用时间、模型、token 消耗量。这条日志就是你的「知情决策」依据——哪个 Skill 在什么时候调了什么模型,一目了然。
再做一个反向验证:把settings.json里test-echo的network_access改成true,然后在index.js里加一行直接fetch外部域名的代码,重新运行。你会看到请求被拦截,报错信息类似:
SkillNetworkError: network access denied for skill 'test-echo'这说明白名单机制在工作。恶意 Skill 即使想外传数据,也会被挡在出口。
5. 本篇常见错排查
配置过程中有几个坑我踩过,列出来帮你省时间。
第一个坑:环境变量没生效导致 401。报错信息通常是Authentication failed: missing api key。先确认echo $TAOTOKEN_API_KEY有输出,再确认 OpenClaw 进程是在设置环境变量之后启动的。如果你用 systemd 或 launchd 托管 OpenClaw,环境变量需要在 service 文件里单独声明,不会自动继承 shell 的配置。
第二个坑:config.toml 里写了明文 Key。有些人图省事直接把api_key = "sk-xxx"写进配置文件,这样api_key_env就失效了,而且配置文件一旦泄露 Key 就暴露。正确做法是只保留api_key_env,Key 永远只存在于环境变量或密钥管理服务里。
第三个坑:Skill 权限条目名称不匹配。settings.json里的name必须和 Skill 目录下skill.json的name完全一致,大小写敏感。不匹配的话,Skill 会被当成unknown_skill直接拒绝,报错是Skill not in allowlist。排查方法是用openclaw skill list看实际注册的名称。
第四个坑:allowlist 漏了 TaoToken 的 API 域名。如果你在config.toml的allowlist里只写了taotoken.net,但实际 API 请求走的是api.taotoken.net,会被拦截。两个都加上,或者用通配符*.taotoken.net(取决于 OpenClaw 版本是否支持通配)。
第五个坑:审计日志目录不存在导致启动失败。audit_log指定的路径如果父目录不存在,OpenClaw 启动时会报错。先手动创建:
mkdir -p ~/.openclaw/logs第六个坑:Skill 更新后权限条目没同步。如果你关掉了auto_update,手动更新 Skill 后记得检查settings.json里的权限条目是否还适用。新版本可能申请了新的权限,旧条目不会自动更新,可能导致 Skill 功能异常或权限过宽。
第七个坑:模型名称写错导致 404。default_model和allowed_models里的模型 ID 必须和 TaoToken 支持的模型列表一致。写错的话报错是Model not found。去 https://taotoken.net/doc 查一下当前支持的模型 ID,别凭记忆写。
6. 把凭证暴露面收进一个口子
ClawHub 这次事件给所有用 OpenClaw 的人提了个醒:Skill 供应链的安全不能指望平台排名,也不能指望开发者自觉。能控制的只有自己这一侧——Key 放在哪、Skill 能碰什么、调用走哪条通道。
这套配置的核心思路就一句话:让 Skill 拿不到 Key,让调用经过审计,让网络出口收窄。TaoToken 在这里的角色是统一入口,把原本散落在各个 Skill 里的模型凭证收敛成一个受控通道。配合 OpenClaw 的权限配置,即使某个 Skill 被投毒,它能造成的破坏也被限制在「一次被记录的模型调用」范围内。
如果你还在用多个 Key 分别配置不同 Skill,建议花半小时按上面的骨架重构一次。长期做编码和 Agent 开发的话,可以看看 Coding Plan 的额度方案,比按次调用更适合高频场景:https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,遇到配置问题可以先翻文档再排查。想快速验证模型通道是否通,用模型对话页面发一条测试消息最直接:https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。
最后留一个实用习惯:每次装新 Skill 之前,先看它的skill.json里申请了哪些权限,再对照settings.json里你给它的授权,多出来的权限一律砍掉。这个动作花不了一分钟,但能挡掉大部分「安静地多做一点事」的 Skill。