一个很常见的误会
@ChrisLuhrsUX 发帖说,他发现自己的两把 API key 被打进了 iOS App 的安装包里,任何人下载 App 都能把它们提取出来,这也解释了为什么每月账单莫名其妙地高。他原以为 .env 已经写进 .gitignore,就万事大吉;而帮他写代码的 AI 助手也没提醒过这一点。
.gitignore 只决定哪些文件不进 Git 仓库。但构建工具在打包时,会把代码里引用到的值原样写进产物。只要你的前端或 App 代码直接读了这把 key,它就会跟着安装包、网页 JS 一起发到每个用户手上,和公开发布没有区别。
Next.js 用户尤其要看一眼
@builtsafely 补充了一个具体的坑:Next.js 会把所有以 NEXT_PUBLIC_ 开头的环境变量复制进访问者下载的 JavaScript。Anthropic 的 key 必须放在不带这个前缀的服务端变量里,只由 API 路由读取。他给的自查方法很简单:构建完成后,在 .next/static 目录里搜索 sk-ant,结果应该是零。
同样的思路适用于其他框架:Vite 的 VITE_ 前缀、各类移动端的配置文件,凡是会进入客户端产物的位置都不能放密钥。
官方怎么说
- Anthropic 的 Claude Academy 课程 明确:不要从客户端代码直接请求 Claude API,任何人都能提取 key;正确做法是 App 请求你自己的服务器,由服务器持有 key 去调用。
- 平台的 Authentication 文档 还列出了一个专门给 iOS、macOS App 的方式 App Attest:只给经过验证的正版安装发放短期令牌,适合没有后端、App 直接调用 API 的场景。
- 帮助中心 API key 最佳实践 建议按用途分开 key、定期轮换,并盯住 Console 的 Logs 和 Usage 页面。
已经发出去了怎么办
- 立刻在 Console 删除这把 key(Settings > API keys),再新建一把只放在服务端。官方说明怀疑泄露时应直接删除。
- 发新版本不等于止血:旧版本安装包还在别人手机和网页缓存里,只有删 key 才算真正关掉。
- 查 Usage 页确认异常用量的时间段;检查自动充值阈值和消费上限,避免继续被刷。
- 以后每次发版前,把「在构建产物里搜 sk-ant」加进发布检查清单。
小结
密钥安全的边界不是「有没有提交到 Git」,而是「有没有离开你的服务器」。让 AI 帮你写客户端代码时,也要主动问一句:这把 key 最后会出现在哪里?
如果你想把 Claude Code 放在一台常开、整机独享的新加坡 Mac mini 上长期运行,可以了解 Zumac.Ai,咨询 Telegram @singatao。
