API Key 怎么管才不泄漏:开发者常犯的几个错
数据截至 2026-07,价格与限额以各官网为准。
API Key 泄漏这件事,绝大多数不是攻击者技术多高,而是密钥被开发者自己放到了不该放的位置——前端代码里、公开仓库的提交历史里、报错日志里、发给同事的截图里。所以防泄漏的重点从来不是”加密算法选哪个”,而是几条很朴素的工程纪律:key 不进代码、一人一把、能随时吊销、出事三分钟内能止损。
先承认一个特别常见的误解:很多人觉得”我这只是个小项目,没人会盯上我的 key”。事实是被扫到跟项目大不大没关系。公开代码托管平台上有大量自动化爬虫在持续扫描新提交,各家平台的密钥又都有相对固定的前缀格式,正则一跑一个准。有人试过把一把废弃的 key 故意推到公开仓库里,被拿去调用的时间往往以分钟计。你的项目有没有人关注不重要,你的 key 能不能换成算力才重要。
泄漏到底发生在哪几个地方
把真实案例归一下类,绝大部分都落在下面这五条路径上,按发生频率从高到低:
- 硬编码进源码,然后进了版本库。写 demo 时图快直接把 key 贴进去,想着”跑通了再改”,结果跑通了就顺手 commit 了。
- 放在前端/客户端。React 项目里放进
.env但被打包进了浏览器包、移动端 App 里写进配置文件——这两种情况下 key 等于公开,任何人打开开发者工具或反编译一下就能拿走。 - 被日志和报错打印出来。调试时打印完整请求头、异常堆栈里带上了请求对象、接了第三方错误上报服务把整个 context 传上去。
- 截图、聊天记录、文档。终端截图里带着环境变量输出,或者在群里发”我这段配置你照着抄”。
- 共享账号与长期不轮换。整个团队共用一把 key,人来人往,离职了也没人记得换。
看清这五条以后你会发现,加密存储、密钥管理服务这些”高级手段”解决的其实是最后一公里的问题,而上面五条基本都属于流程和习惯。先把流程堵住,收益最大。
第一条纪律:key 只进环境变量,不进代码
这条是底线,具体怎么落地:
把密钥放进项目根目录的 .env 文件,代码里通过环境变量读取,比如 Python 里 os.getenv("XXX_API_KEY")、Node 里 process.env.XXX_API_KEY。然后第一件事是把 .env 写进 .gitignore——注意顺序,很多人是先建了 .env、提交了一次、才想起来加 .gitignore,那时候已经晚了。
同时建一个 .env.example 提交进仓库,里面只写变量名不写值:
# .env.example —— 这个文件可以提交,里面不放真实值
OPENAI_API_KEY=
ANTHROPIC_API_KEY=
CUSTOM_GATEWAY_BASE_URL=http://localhost:20128/v1
这样新同事拉下代码,cp .env.example .env 填上自己的 key 就能跑,既不用口口相传”还有哪些变量要配”,也不会有人被迫去问别人要一把现成的 key。
生产环境则不要用 .env 文件,交给部署平台的密钥注入机制(容器编排的 secret、云平台的配置中心、CI 的加密变量)。判断标准很简单:这个值有没有可能被 cat 出来、被打包进镜像层、被备份带走。
提交历史里的 key,删掉不等于没了
这是最容易被低估的一条。有人发现自己把 key 提交了,赶紧改一行代码删掉再推一次,然后就当没事了。但 Git 是全量历史,那把 key 在旧 commit 里原封不动地躺着,clone 一下就能翻出来。
正确的处置顺序是:
- 立刻去平台吊销这把 key,重新生成一把。这一步优先级最高,几分钟内完成,不要等清理历史。
- 再考虑清理历史。用
git filter-repo之类的工具重写历史是可行的,但如果仓库已经被别人 fork 或 clone 过,重写本地历史并不能收回已经流出去的副本。 - 检查这段时间的用量记录,看有没有不是你发起的调用。
记住次序:吊销永远比清理重要。历史清理是为了避免以后再被翻出来,吊销才是真正让泄漏的那串字符失效。日常可以在提交前挂一道自动检查,开源的密钥扫描工具(gitleaks、git-secrets 这类)能做成 pre-commit 钩子,在你 commit 的那一刻就拦下来,具体用法以各工具官方文档为准。
一人一把、一环境一把
共用一把 key 的问题不在于”不安全”这个抽象说法,而在于出事时你没有任何腾挪空间。一旦怀疑泄漏,你只能吊销唯一那把,然后所有人、所有环境同时停摆,再一个个重新配置。
分开发之后,成本只是多建几把 key,好处是:
- 谁的 key 被滥用,看用量就能定位到人或到服务,不用全员排查。
- 吊销一把只影响一个环境,线上服务不受开发环境事故牵连。
- 有人离职、某台开发机丢了,吊销对应那把就行。
命名上养成习惯,创建时就按用途写清楚,比如 prod-backend、ci-test、local-zhangsan。多数平台的 key 创建后只完整显示一次,关掉弹窗就看不到明文了,所以名字要在创建当下就取对,事后靠猜”这把是干嘛的”会很难受。
部分平台还提供权限范围限制、消费上限、调用来源限制这类控制手段,能用就用,具体有哪些选项、怎么设置以各平台官方最新说明为准。
日志和报错:别把 key 打出去
这条在代码评审时最容易漏掉,因为出问题的往往不是你写的那行,而是框架和第三方库的默认行为。几个具体做法:
- 不要打印完整请求头。调试时想看请求,也只打印
Authorization的前几位加长度,比如sk-***(已脱敏),足够确认”读到了/没读到”,又不会泄漏内容。 - 异常处理里不要把整个 request 对象序列化进日志。很多 HTTP 客户端的异常信息默认会带上请求详情。
- 接错误上报平台前先确认脱敏规则。把线上异常自动上报到第三方服务是好习惯,但要确认上报的 payload 里不含凭证字段。
- 写一条脱敏中间件,统一把配置好的敏感字段名(key、token、secret、password、authorization)在落盘前替换掉,比指望每个人自觉靠谱。
顺带说个高频误判:日志里看到 None 或者空字符串就以为是”鉴权失败”,其实往往是环境变量根本没读进当前进程。这种时候用上面那种只打前几位加长度的方式,一眼就能分清是”没读到”还是”读到了但不对”。
把凭证交给第三方工具之前,先问三个问题
现在的 AI 开发工具链里有一类东西绕不开:本地 AI 网关。以 MIT 协议的开源网关 OmniRoute 为例,它的思路是让所有 AI IDE 和 CLI 统一指向一处配置 http://localhost:20128/v1,由网关去对接后端的各家供应商;它由 9router fork 而来,是 Go 项目 CLIProxyAPI 的 TypeScript 移植,仓库自述聚合了 290+ 供应商、500+ 模型。这类工具确实省事,但它天然意味着你把各家的凭证集中交给了一个本地代理进程,风险敞口从”分散在各工具”变成了”集中在一处”。
所以在装之前建议先问三个问题:
第一,它拿到的是哪一类凭证? OmniRoute 的认证方式分四种:OAuth(由网关代管登录,不需要你交出 API key)、web cookie、API key(付费供应商,可能带免费额度)、以及 Local(对接 Ollama、LM Studio、vLLM 这类本地推理)。这四种的敏感度完全不同——Local 根本不涉及外部凭证,OAuth 至少不用你手动交 key,而直接填 API key 的方式敞口最大。文档提到免费供应商无需凭证即可直接 Connect,如果只是想先试试效果,从这类不需要交凭证的入口开始,是风险最低的试水方式。
第二,凭证存在哪、会不会外发? 开源的好处就是这件事可以自己查,而不是听宣传。装完可以先跑一下 omniroute doctor 做自检,同时留意配置文件落在磁盘的什么位置、权限如何。真正要紧的是搞清楚:这个进程只在本机监听,还是有任何形式的外发上报。
第三,出事了能不能快速止损? 对应回前面那条纪律——给这类工具单独发一把 key,别把生产环境那把塞进去。真出问题时你只需要吊销这一把。
关于免费额度还要多说一句:这类信息变动极快,仓库里写的”永久免费""无限免费”应当理解为当时的仓库自述而非承诺,具体以仓库文档当前版本和各供应商官方说明为准,别把它当成可以写进商业计划的成本假设。
还有一件必须诚实说明的事:OpenAI、Anthropic、Google 等海外供应商官方并不支持中国大陆直连,这是准入层面的现实,不是网络快慢的问题。市面上确实存在第三方中转和聚合服务,但其合规性、计费透明度、数据处理方式都需要使用者自行核实并承担风险,本文不做推荐也不背书——尤其要注意,把 key 交给来路不明的中转服务,等于同时放弃了凭证控制权和调用内容的私密性。
已经泄漏了,按这个顺序处置
不要慌,也不要先纠结”是谁干的”。按顺序做:
- 吊销。到平台把这把 key 删掉,几分钟内完成。
- 重建并更新部署。新 key 走正常的密钥注入流程,不要为了赶时间又硬编码回去。
- 查用量。看有没有异常调用,判断损失范围。
- 堵源头。把泄漏路径找出来(是 commit?是截图?是日志?),补上对应的防线,比如加 pre-commit 扫描。
- 复盘记录。写清楚发生了什么、改了什么,团队内部同步一次,比通报批评有用得多。
局限与不适用场景
要诚实说几条边界:
- 纯前端/纯客户端应用没有安全存放 key 的办法。不管你用什么混淆、什么加密,最终解密的代码就在用户手里。这种场景的唯一正解是加一层自己的后端做代理,key 只存在服务端。所谓”前端安全存储 key”的方案基本都是自欺欺人。
- 个人玩具项目不必上重型方案。环境变量加
.gitignore加 pre-commit 扫描已经覆盖了绝大部分风险,密钥管理服务、自动轮换这些是团队和生产环境的事,个人项目上了反而增加摩擦、更容易被绕过。 - 自动轮换不是万能的。如果泄漏路径本身没堵住(比如日志一直在打印),轮换只是把泄漏频率变高了。先堵路径,再谈轮换周期。
- 本地网关不等于更安全。它降低的是”多处配置”的管理成本,不是”凭证被拿走”的风险;集中之后单点被攻破的后果反而更重。
小结
API Key 的安全,九成靠工程纪律,一成靠工具。key 只进环境变量、.gitignore 先于 .env 建立、一人一环境一把、日志强制脱敏,这四条做到就避开了绝大多数真实泄漏。发现泄漏时第一动作永远是吊销而不是删代码,因为提交历史里的东西删不干净。把凭证交给任何第三方工具或本地网关之前,先确认它拿的是哪类凭证、存在哪、能不能单独吊销,能用不需要交凭证的方式试水就先试水。至于各类免费额度和访问渠道的说法,一律以官方最新说明为准,不要当成稳定假设写进方案。