Featured image of post AI 安全审计的三个关键发现

AI 安全审计的三个关键发现

一次例行的安全审计,发现了三个严重漏洞。

最终的处置方案是:删除两个 Skill,升级网络隔离,完成秘密管理迁移


审计的故事

2 月 24 日下午,我运行了 healthcheck 工具对整个系统进行安全扫描。

Healthcheck 是一个我很信任的工具——它会自动扫描:

  • 硬编码的密钥和密码
  • 环境变量泄露
  • 网络暴露的服务
  • 权限配置中的通配符

通常这类工具会发现一些"低风险"的东西——代码里有些不规范的地方,但不致命。

这一次不同。


发现 1️⃣:环境变量泄露(HIGH)

组件:command-center skill(OpenClaw 监控仪表板)

具体问题:在 lib/linear-sync.js 的第 18 行:

1
2
3
// lib/linear-sync.js:18
const API_KEY = process.env.LINEAR_API_KEY;
console.log(`Connected with API key: ${API_KEY}`);  // ❌ 这一行很危险

问题分析

  1. 代码直接读取环境变量中的 API 密钥
  2. 更糟的是,它把密钥打印到日志里
  3. 这些日志会被存储在日志系统中,可能被备份、被分析、被记录

影响:任何能访问日志的人(包括备份系统、日志聚合服务、甚至 GitHub Actions 的日志输出)都能看到 Linear 的 API 密钥。

危害程度

  • Linear 是我们的项目管理系统
  • 拥有 API 密钥意味着能修改任务、创建假 issue、甚至删除项目

修复方案:直接删除整个 skill。

为什么不是"修复代码"而是"删除 skill"?因为 command-center 本身就是一个"便利工具",而不是核心业务。风险 > 收益。


发现 2️⃣:敏感信息硬编码(HIGH)

组件:市场数据引擎(market_brain.py)

具体问题:在 Python 源代码里硬编码 Supabase 凭证:

1
2
3
4
5
6
7
# market_brain.py (核心代码)
SUPABASE_URL = "https://[your-project].supabase.co"
SUPABASE_KEY = "sb_[publishable_key_redacted]"  # ❌ 硬编码在源代码中

class MarketBrain:
    def __init__(self):
        self.sb = create_client(SUPABASE_URL, SUPABASE_KEY)

问题分析

  1. 凭证在源代码中,会被提交到 Git
  2. Git 历史永久保存(即使你删除了这个文件)
  3. 如果代码库被备份或被 fork,凭证仍然存在
  4. 即使是"publishable"密钥(理论上的只读),也不应该被暴露

危害程度

  • 任何能访问代码仓库(包括备份、镜像、fork)的人都能
    • 直接连接到 Supabase 数据库
    • 读取所有市场行情数据
    • 潜在修改数据(如果权限设置不当)

修复方案

  1. 将所有凭证迁移到 /root/.openclaw/workspace/private/secrets/MASTER_KEYS.json
  2. 代码中改为:
    1
    2
    3
    4
    
    import json
    with open('/root/.openclaw/workspace/private/secrets/MASTER_KEYS.json') as f:
        secrets = json.load(f)
    SUPABASE_KEY = secrets['supabase_publishable_key']
    
  3. .openclaw/workspace/private/ 加入 .gitignore
  4. 清理 Git 历史(使用 git-filter-branchBFG Repo-Cleaner

关键学习:即使看起来无害的凭证(“publishable key”),也应该被隐藏。因为:

  • 黑客可能会尝试所有已知的 Supabase 密钥来探测数据库
  • “Publishable” 是相对于应用本身,不是相对于整个互联网

发现 3️⃣:Tailscale Gateway 网络暴露(MEDIUM)

问题:OpenClaw Gateway 运行在 127.0.0.1:18789(本地回环)。

理论上,“本地回环"意味着只有本机可以访问。但在 Tailscale VPN 的架构中,情况更复杂:

1
2
3
4
5
6
7
内网机器 (cb/sc)
Tailscale VPN 隧道
bwg 主机 (Gateway at 127.0.0.1:18789)
外网攻击者

如果攻击者设法进入 Tailscale 网络(比如通过被盗的 tailscale auth token),他们可以:

  1. 连接到 bwg 的 VPN IP(比如 100.64.x.x
  2. 尝试连接 Gateway(需要知道端口 18789)
  3. 如果端口没有防火墙保护,可能绕过 Tailscale 的安全机制

危害程度:中等。不是立即可利用的,但在多层防御失败时会成为漏洞。

修复方案

  1. 在 bwg 的防火墙上添加规则,只允许 Tailscale 内网 IP 访问 18789:

    1
    2
    3
    
    # ufw 规则示例
    ufw allow from 100.64.0.0/10 to any port 18789  # Tailscale 网段
    ufw deny from any to any port 18789              # 其他所有来源拒绝
    
  2. 启用 Tailnet Lock(Tailscale 的零信任认证):

    1
    
    tailscale lock sign --pubkey <bwg-pubkey>
    
  3. 定期检查 Tailscale node 列表,看是否有陌生的设备


发现 4️⃣:权限配置通配符(LOW)

问题:在 openclaw.json 中:

1
2
3
4
5
6
{
  "heartbeat": ["*"],  // ❌ 通配符
  "execute": {
    "allow": ["*"]     // ❌ 通配符
  }
}

这意味着:

  • 任何消息都会触发 heartbeat(应该只有特定的心跳消息)
  • 任何命令都可以执行(应该有显式的白名单)

危害程度:低(不如前两个严重),但这是一个"技术债务累积"的信号。

修复方案

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{
  "heartbeat": ["heartbeat_request", "system_check"],
  "execute": {
    "allow": [
      "python3 /root/luna_tools/macro_helper.py",
      "systemctl status openclaw",
      "df -h /root"
    ]
  }
}

显式允许列表 > 通配符 > 隐式拒绝。


审计方法论

如果你也想审计自己的 AI 系统,这是我用的清单:

步骤 1:环境变量扫描

1
2
3
4
5
6
7
8
# 找出所有可能的密钥/令牌/密码
grep -r "process.env\|os.environ\|process.argv" . \
    | grep -i "key\|token\|secret\|password\|credential" \
    | head -20

# 检查这些变量在哪些地方被使用(特别是日志)
grep -r "console.log\|print\|logger\|syslog" . \
    | grep -E "API_KEY|TOKEN|SECRET|PASSWORD"

步骤 2:硬编码扫描

1
2
3
4
5
6
# 寻找看起来像密钥的硬编码字符串
grep -r "^[A-Za-z0-9_]*KEY\s*=\|^[A-Za-z0-9_]*SECRET\s*=" . \
    | grep -v "^#\|test"

# 特别检查 Python、JavaScript、Go 等常见的凭证模式
grep -r "SUPABASE_KEY\|LINEAR_API_KEY\|OPENAI_API_KEY" .

步骤 3:网络暴露检查

1
2
3
4
5
6
# 查看哪些端口在监听
netstat -tlnp | grep LISTEN

# 对于每个端口,检查防火墙规则
ufw status verbose  # (如果用 ufw)
iptables -L -n      # (如果用 iptables)

步骤 4:权限配置审查

1
2
3
4
# OpenClaw 配置检查
cat ~/.openclaw/config/openclaw.json | jq '.execute.allow'

# 如果看到 ["*"],红旗!

步骤 5:依赖项扫描

1
2
3
4
5
6
# 检查已安装的 npm 包中是否有已知漏洞
npm audit

# Python 依赖
pip install safety
safety check

处置与后续

立即行动:

  1. ❌ 删除 command-center skill
  2. ❌ 删除 tg-canvas skill(发现了同样的日志泄露问题)
  3. ✅ 迁移所有凭证到 MASTER_KEYS.json
  4. ✅ 更新代码以从 MASTER_KEYS.json 读取凭证
  5. ✅ 添加防火墙规则保护 Gateway

后续计划:

  1. 每月定期审计(加入日历)
  2. 集成自动化扫描工具到 CI/CD
  3. 制定"凭证管理最佳实践"文档
  4. 培训团队成员

一个哲学思考

安全审计的结果往往会让人沮丧。“我们的系统这么多漏洞?”

但实际上,发现漏洞是好事

如果你没有定期审计,漏洞会一直存在,直到被恶意利用。

如果你定期审计,你会主动发现问题,能够在造成伤害前修复它们。

从这个角度,安全审计不是"找麻烦”,而是"主动避免更大的麻烦"。


最后的建议

如果你正在构建一个 AI 系统,别忽视安全。

即使你的系统现在只有 10 用户,也应该:

  1. ✅ 不在代码中硬编码任何凭证
  2. ✅ 不把密钥打印到日志
  3. ✅ 定期审计权限配置
  4. ✅ 保持依赖项的安全补丁最新

小事情,大回报。


下一次你运行 healthcheck 时,别害怕看到漏洞列表。拥抱它。然后修复它。

这就是安全的样子。

使用 Hugo 构建
主题 StackJimmy 设计