前几篇一直在聊 AI API 的 Token 消耗、Key 隔离、预算、告警和限流。这些内容更多关注的是Token 用了多少 哪个项目消耗最多 怎么限制异常消耗 怎么做预算和告警但在实际项目里还有一个同样重要的问题API Token 安全。很多 AI 项目在开发阶段大家更关心接口能不能调通。但项目一旦上线API Token 就不只是一个配置项而是一个非常敏感的凭证。如果 Token 泄露可能会带来几个问题被别人盗用额度 导致成本异常增加 影响线上服务稳定 暴露项目调用能力 很难追踪是谁泄露的 旧 Token 长期遗留在代码或日志里所以这篇继续围绕 AI API Token聊聊实际项目里应该怎么管理 Token 安全。一、API Token 本质上就是访问凭证很多人会把 API Token 当成普通配置。比如AI_API_KEYsk-xxxxxxxxxxxxxxxx但实际上它更像是一把钥匙。谁拿到这把钥匙谁就能调用对应的服务。如果这个 Token 没有限制它可能具备完整调用权限。如果额度没有限制它还可能造成持续消耗。所以在项目里Token 至少要按敏感信息处理不能随便写在前端代码 Git 仓库 接口返回 日志文件 截图文档 群聊消息 公开配置文件这些地方都是比较常见的泄露来源。二、最常见的 Token 泄露方式1. 直接写进代码开发阶段为了方便有些人会这样写const API_KEY sk-xxxxxxxxxxxxxxxx;短期看确实方便复制代码就能跑。但只要代码提交到仓库这个 Token 就存在泄露风险。即使后面删除了Git 历史里可能仍然存在。所以不建议把 Token 直接写进源码。更推荐使用环境变量const API_KEY process.env.AI_API_KEY;对应配置AI_API_KEYsk-xxxxxxxxxxxxxxxx同时要确保.env不会被提交.env .env.local .env.production2. 把 Token 暴露到前端这是非常常见的问题。比如前端直接调用 AI APIfetch(https://api.example.com/v1/chat/completions, { method: POST, headers: { Authorization: Bearer sk-xxxxxxxxxxxxxxxx, Content-Type: application/json }, body: JSON.stringify({ model: xxx, messages: [{ role: user, content: 你好 }] }) });这种写法非常危险。因为前端代码会被浏览器下载用户可以通过开发者工具看到请求头也能看到 Token。正确做法是前端 - 自己的后端 - AI APIToken 只保存在后端由后端代为请求模型服务。前端只调用自己的接口例如await fetch(/api/ai/chat, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ message: 帮我总结这段内容 }) });后端再去调用 AI 服务app.post(/api/ai/chat, async (req, res) { const response await fetch(process.env.AI_BASE_URL, { method: POST, headers: { Authorization: Bearer ${process.env.AI_API_KEY}, Content-Type: application/json }, body: JSON.stringify({ model: chat-model, messages: [ { role: user, content: req.body.message } ] }) }); const data await response.json(); res.json(data); });这样 Token 不会暴露给浏览器。3. 日志里打印了完整 Token有些项目为了调试会把请求头打印出来。例如console.log(headers:, headers);如果 headers 里有 Authorization就可能把 Token 打到日志系统里。日志系统可能被多人查看也可能长期保存。比较好的做法是对敏感字段脱敏function maskToken(token) { if (!token) return ; return token.slice(0, 6) **** token.slice(-4); }打印时console.log(api token:, maskToken(apiKey));输出类似api token: sk-abc****9xyz这样既方便排查又不会泄露完整 Token。4. 把 Token 发到群里或文档里开发协作时经常会有人为了方便把 Token 直接发到群里你先用这个 Key 测试一下sk-xxxxxx或者写进文档测试 Keysk-xxxxxx这种方式也有风险。因为群聊和文档通常权限比较宽后期也很难清理。更好的方式是每个人或每个项目分配独立 Token 不在群里发送完整 Token 需要测试时生成临时 Token 测试完成后及时停用三、Token 不应该所有项目共用一个如果所有项目都使用同一个 Token就会出现几个问题。无法区分哪个项目在调用 无法单独限制某个项目 泄露后影响范围很大 无法快速判断异常来源 更换 Token 时所有项目都要改比如一个团队有这些项目知识库问答 AI 写作助手 自动摘要脚本 客服机器人 本地测试工具如果全部共用一个 TokenAI_API_KEYteam_shared_key一旦这个 Token 泄露所有能力都会暴露。更合理的方式是分开prod_kb_qa prod_writer_api batch_summary_job prod_customer_service dev_local_test每个 Token 对应一个明确用途。这样如果batch_summary_job出现异常只需要暂停这个 Token不会影响其他项目。四、Token 权限也应该分级很多项目里的 Token 只有一种权限能调用就行。但如果项目变复杂最好给 Token 做权限分级。例如只允许聊天模型 只允许总结任务 只允许低成本模型 只允许测试环境调用 只允许某个项目调用 只允许每天固定额度 只允许固定 IP 使用可以设计成类似这样的权限结构{ token_name: batch_summary_job, project: content_center, env: prod, allowed_models: [summary-model], allowed_tasks: [article_summary], daily_token_limit: 500000, status: active }这样 Token 即使被误用也不会获得所有能力。比如批量摘要任务的 Token就不应该能调用所有模型也不应该能访问其他项目的能力。五、Token 命名要能看出用途Token 命名不要太随意。不建议这样命名key1 test demo newkey api mytoken时间久了完全不知道这些 Token 是干什么的。比较推荐的命名方式环境_项目_用途例如prod_kb_qa_chat prod_writer_generate batch_article_summary test_admin_tool dev_local_debug如果团队成员多也可以加负责人dev_script_zhangsan test_bot_lisi batch_summary_ops这样一看名字就能大概判断用途。当发现某个 Token 消耗异常时也更容易定位。六、软性插一句工具实践前面这些 Token 隔离、权限控制、用量统计和异常追踪如果项目数量少可以自己在业务系统里做。但项目一多比如既有知识库问答又有写作工具、批量脚本、团队成员共享自己维护 Token 管理就会越来越麻烦。我最近体验的斑马 API 这类 AI API 统一入口工具比较适合把这些 Token 管理能力集中起来。比如不同项目分配不同 Key批量任务单独 Key再结合用量统计看每个 Key 的 Token 消耗排查时会比所有项目共用上游 Key 清楚很多。目前新用户有体验权益邀请新用户也有额外体验时长。https://bmapi.020212.xyz/register?affYU55ECFS8AF2七、Token 要支持停用而不是只能删除Token 管理里我觉得“停用”比“删除”更重要。因为有些 Token 你不确定是否还在使用。如果直接删除可能会影响线上服务。更稳妥的流程是先标记观察 再停用 观察一段时间 确认无影响后删除例如old_demo_key 最近 60 天无调用 先设置为 disabled 观察 7 天 没有人反馈异常 再删除或归档这样比直接删除安全。Token 状态可以分成几种active正常使用 disabled已停用 expired已过期 rotating轮换中 archived已归档八、Token 轮换机制很重要Token 不应该永久不变。尤其是生产环境 Token最好定期轮换。常见轮换场景包括成员离职 Token 疑似泄露 项目权限调整 Token 使用时间过长 代码仓库曾经暴露过 日志里出现过完整 Token轮换时不要直接替换否则容易导致服务中断。更推荐双 Token 过渡。轮换流程示例1. 创建新 Token 2. 新旧 Token 同时可用 3. 业务配置切换到新 Token 4. 观察调用日志 5. 确认旧 Token 无调用 6. 停用旧 Token 7. 一段时间后删除旧 Token这样可以降低风险。九、一个 Token 轮换示例假设当前生产服务使用prod_kb_qa_old现在要轮换成prod_kb_qa_new流程可以是第一步创建 prod_kb_qa_new 第二步保持 prod_kb_qa_old 可用 第三步修改生产环境变量 第四步重启服务 第五步观察日志里是否还有 old Token 调用 第六步确认没有旧调用后停用 old Token如果发现旧 Token 还有请求prod_kb_qa_old 仍有调用 来源 IP某台旧服务器说明还有服务没有更新配置。这时候就不要急着删除旧 Token而是先找到来源。十、Token 调用日志应该记录什么为了排查 Token 问题日志里要记录必要字段。例如{ request_id: req_001, token_name: prod_kb_qa_chat, project: knowledge_base, env: prod, model: chat-model, prompt_tokens: 1200, completion_tokens: 300, total_tokens: 1500, status_code: 200, ip: 10.0.0.12, created_at: 2025-01-01 10:00:00 }注意日志里不要记录完整 Token。只记录 Token 名称、ID 或脱敏后的 Token 即可。可以记录token_name token_id token_prefix不要记录完整 token 字符串 完整 Authorization 请求头十一、如何发现 Token 被盗用Token 被盗用不一定立刻能发现但可以通过一些异常信号判断。例如请求量突然上涨 Token 消耗突然变高 出现陌生 IP 调用时间异常 调用模型异常 错误率异常 某个旧 Token 突然恢复调用 开发 Token 在生产高频调用举个例子dev_local_debug 正常每天消耗 1 万 tokens 某天突然消耗 80 万 tokens 请求来源不是公司或服务器 IP 调用时间集中在凌晨这时候就需要怀疑 Token 是否泄露。可以先做几件事立即停用该 Token 查看最近调用日志 确认来源 IP 和调用时间 通知相关负责人 创建新 Token 并限制权限 检查代码仓库和日志是否泄露十二、Token 最好绑定使用范围如果条件允许可以给 Token 增加使用范围限制。例如绑定 IP 绑定项目 绑定环境 绑定模型 绑定任务类型 绑定每日额度 绑定过期时间比如开发环境 Token{ token_name: dev_local_test, env: dev, daily_token_limit: 50000, allowed_models: [cheap-model], expires_at: 2025-02-01 }批量任务 Token{ token_name: batch_translate_job, env: prod, allowed_tasks: [translate], daily_token_limit: 300000, rate_limit: 60 requests/min }生产聊天 Token{ token_name: prod_chat_api, env: prod, allowed_models: [chat-model, fast-model], daily_token_limit: 2000000 }这样 Token 的风险范围会小很多。十三、不要把所有能力都放在一个超级 Token 上有些项目会创建一个“万能 Token”。它可以调用所有模型 访问所有任务 没有额度限制 没有过期时间 没有环境限制 所有项目共用这种 Token 用起来方便但风险很高。一旦泄露影响范围最大。更合理的是拆分线上业务 Token 批量任务 Token 测试环境 Token 开发环境 Token 管理员工具 Token 临时调试 Token每个 Token 只做自己该做的事情。这其实就是最小权限原则。十四、临时 Token 一定要有过期时间临时测试经常需要发一个 Token 给别人用。比如帮我测一下这个接口 临时跑一下这个脚本 给外部同事验证一下功能这种场景最好创建临时 Token并设置过期时间。例如temp_test_202501 有效期7 天 每日额度20,000 tokens 仅允许测试模型不要把正式生产 Token 发出去。临时 Token 到期后自动失效就算忘记清理风险也比较可控。十五、前端项目一定要检查构建产物有时开发者以为 Token 没有写进前端但可能通过环境变量被打进构建产物。比如在 Vite、Next.js 等前端项目里某些前缀的环境变量会暴露给浏览器。例如VITE_AI_API_KEYsk-xxxxxxxx如果前端代码里使用了它构建后就可能出现在 JS 文件中。所以前端项目里不要放服务端 Token。可以做一个简单原则浏览器能看到的都不应该放真实 AI API Token前端只拿用户会话 token真实 AI API Token 放后端。十六、代码提交前做一次 Token 扫描为了避免误提交可以在提交前做检查。比如检查代码中是否包含类似sk- Bearer AI_API_KEY Authorization可以写一个简单脚本import fs from fs; import path from path; const keywords [sk-, AI_API_KEY, Authorization: Bearer]; function scanFile(filePath) { const content fs.readFileSync(filePath, utf-8); return keywords.some(keyword content.includes(keyword)); } function scanDir(dir) { for (const item of fs.readdirSync(dir)) { const fullPath path.join(dir, item); const stat fs.statSync(fullPath); if (stat.isDirectory()) { if ([node_modules, .git, dist].includes(item)) continue; scanDir(fullPath); } else { if (scanFile(fullPath)) { console.log(可能包含敏感信息, fullPath); } } } } scanDir(process.cwd());这只是一个很简单的示例但能发现一些低级错误。更完整的方案可以接入 CI 检查或密钥扫描工具。十七、Token 管理的一张检查清单可以用下面这个清单检查项目是否存在风险Token 是否写进了源码 Token 是否出现在前端代码 Token 是否提交到 Git 历史 Token 是否出现在日志里 Token 是否被发到群聊或文档 不同项目是否共用同一个 Token 开发、测试、生产是否共用 Token 批量任务是否单独 Token Token 是否有额度限制 Token 是否有权限范围 临时 Token 是否有过期时间 生产 Token 是否定期轮换 旧 Token 是否定期清理 异常调用是否能追踪到来源如果很多问题答案都是“不确定”就说明 Token 管理还需要补强。十八、总结AI API Token 管理不只是成本问题也是安全问题。从实践角度看我会优先关注这些点不要把 Token 写进源码 不要把 Token 暴露给前端 不要在日志里打印完整 Token 不要多个项目共用同一个 Token 不要给临时任务使用生产 Token 不要让 Token 永久有效 不要创建无限权限的超级 Token更推荐的方式是按项目创建 Token 按环境创建 Token 按任务创建 Token 给 Token 设置额度 给 Token 设置权限范围 给临时 Token 设置过期时间 定期轮换生产 Token 记录 Token 调用日志 发现异常可以快速停用AI API 接入本身并不难真正需要长期维护的是 Token、额度、权限、安全和成本。如果这些基础能力一开始没有设计好后期项目越多排查和治理成本就越高。所以Token 管理最好从项目早期就开始规范而不是等到泄露、异常消耗或线上故障后再补。