一把 key 散落在多台机器上:如何不让聚合站 key 进 git、shell 历史和前端打包
聚合站的无登录长期 key 虽然方便,但容易在 .env、shell 历史、前端打包和 CI 日志中泄漏。本文梳理这四个常见泄漏面,并给出用轮换习惯替代按人登录的具体做法,帮助你在享受一把 key 调多模型的同时,把风险控制在可接受范围内。
为什么无登录 key 的泄漏面更值得关注
聚合站通常不要求按人登录,而是签发一把长期有效的 API key。好处是接入简单,坏处是这把 key 一旦泄漏,攻击者不需要绕过账号体系就能直接消耗你的额度。没有登录会话、没有二次验证,泄漏即等于失守。因此,与其依赖“没人会拿到”,不如从工程习惯上减少 key 出现的场合。
下面按常见泄漏面逐一说明。
.env 与配置文件:最容易被提交的载体
把 key 放在 .env 里本身没问题,问题在于 .env 经常被误提交。
- 项目初始化时就把
.env加入.gitignore,并提交一份.env.example只放占位符。 - 用
git secrets、gitleaks等工具在 pre-commit 阶段扫描疑似 key 的字符串。 - 不要把 key 写进
docker-compose.yml、config.json这类会被版本控制的文件;用环境变量注入。 - 如果 key 已经进入 git 历史,仅删除文件不够,需要重写历史并立即轮换。
Shell 历史:一次 curl 就可能留下明文
调试时直接 curl -H "Authorization: Bearer sk-..." 很常见,这条命令会进入 ~/.bashhistory 或 ~/.zshhistory。
- 调试时把 key 放进环境变量,命令里只引用变量名,例如
curl -H "Authorization: Bearer $API_KEY"。 - 在命令前加空格并配置
HISTCONTROL=ignorespace(bash)或setopt histignorespace(zsh),让带前缀的命令不写入历史。 - 定期检查历史文件里是否有
sk-开头的字符串,发现后清理并轮换。 - 使用支持 secret 管理的 shell 插件或密码管理器注入,而不是手动 export 明文。
前端打包:key 一旦进 bundle 就是公开的
任何被打进前端 JavaScript 的 key 都等同于公开,因为浏览器端代码对用户完全可见。
- 聚合站 key 只应出现在服务端或受控的构建环境中,不要放在
VITE、NEXTPUBLIC_这类会被注入前端的变量前缀下。 - 前端需要调用模型时,走自己的后端代理,由后端持有 key 并做鉴权与限流。
- 构建产物上线前用工具扫描 bundle 中是否出现疑似 key 的字符串。
- 如果确实需要浏览器直连,考虑短期令牌方案,而不是长期 key。
CI 变量:日志和 fork PR 是两条泄漏路径
CI 里存放 key 看似安全,实际有两个常见出口:日志和不受信任的代码。
- 把 key 存为 CI 的 secret 变量,不要写在 workflow 文件里。
- 避免在 CI 脚本中
echo或set -x打印包含 key 的命令;多数 CI 会对 secret 做掩码,但掩码不是万无一失。 - 对来自 fork 的 PR 默认不注入 secret,防止恶意代码读取环境变量。
- 限制 secret 的作用范围,按环境(开发、预发、生产)使用不同的 key。
用轮换习惯替代按人登录
无登录体系下,轮换是最实际的补偿控制。
- 给不同用途签发不同的 key:本地开发、CI、生产服务各一把,泄漏时影响面可控。
- 设定固定的轮换周期,例如按季度或按项目阶段更换;轮换后旧 key 及时吊销。
- 在聚合站控制台为 key 设置可识别的备注名,便于定位是哪一把泄漏。
- 记录 key 的签发时间和用途,避免出现“不知道这把 key 还用在哪”的情况。
- 一旦怀疑泄漏,先吊销再排查,不要等到确认。
一个可落地的最小清单
.env进.gitignore,提交.env.example。- pre-commit 扫描疑似 key。
- shell 历史不记录含 key 的命令。
- 前端不出现长期 key,统一走后端代理。
- CI secret 按环境隔离,fork PR 不注入。
- 按用途分 key,定期轮换,怀疑即吊销。
这些习惯不依赖登录体系,也不需要额外基础设施,但能显著降低长期 key 在常见工程流程中泄漏的概率。