前几篇一直在聊 AI API 的 Token 消耗、Key 隔离、预算、告警和限流。

这些内容更多关注的是:

Token 用了多少?
哪个项目消耗最多?
怎么限制异常消耗?
怎么做预算和告警?

但在实际项目里,还有一个同样重要的问题:API Token 安全

很多 AI 项目在开发阶段,大家更关心接口能不能调通。
但项目一旦上线,API Token 就不只是一个配置项,而是一个非常敏感的凭证。

如果 Token 泄露,可能会带来几个问题:

被别人盗用额度
导致成本异常增加
影响线上服务稳定
暴露项目调用能力
很难追踪是谁泄露的
旧 Token 长期遗留在代码或日志里

所以这篇继续围绕 AI API Token,聊聊实际项目里应该怎么管理 Token 安全。


一、API Token 本质上就是访问凭证

很多人会把 API Token 当成普通配置。

比如:

AI_API_KEY=sk-xxxxxxxxxxxxxxxx

但实际上,它更像是一把钥匙。

谁拿到这把钥匙,谁就能调用对应的服务。

如果这个 Token 没有限制,它可能具备完整调用权限。
如果额度没有限制,它还可能造成持续消耗。

所以在项目里,Token 至少要按敏感信息处理,不能随便写在:

前端代码
Git 仓库
接口返回
日志文件
截图文档
群聊消息
公开配置文件

这些地方都是比较常见的泄露来源。


二、最常见的 Token 泄露方式

1. 直接写进代码

开发阶段为了方便,有些人会这样写:

const API_KEY = "sk-xxxxxxxxxxxxxxxx";

短期看确实方便,复制代码就能跑。
但只要代码提交到仓库,这个 Token 就存在泄露风险。

即使后面删除了,Git 历史里可能仍然存在。

所以不建议把 Token 直接写进源码。

更推荐使用环境变量:

const API_KEY = process.env.AI_API_KEY;

对应配置:

AI_API_KEY=sk-xxxxxxxxxxxxxxxx

同时要确保 .env 不会被提交:

.env
.env.local
.env.production

2. 把 Token 暴露到前端

这是非常常见的问题。

比如前端直接调用 AI API:

fetch("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 API

Token 只保存在后端,由后端代为请求模型服务。

前端只调用自己的接口,例如:

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

或者写进文档:

测试 Key:sk-xxxxxx

这种方式也有风险。

因为群聊和文档通常权限比较宽,后期也很难清理。

更好的方式是:

每个人或每个项目分配独立 Token
不在群里发送完整 Token
需要测试时生成临时 Token
测试完成后及时停用

三、Token 不应该所有项目共用一个

如果所有项目都使用同一个 Token,就会出现几个问题。

无法区分哪个项目在调用
无法单独限制某个项目
泄露后影响范围很大
无法快速判断异常来源
更换 Token 时所有项目都要改

比如一个团队有这些项目:

知识库问答
AI 写作助手
自动摘要脚本
客服机器人
本地测试工具

如果全部共用一个 Token:

AI_API_KEY=team_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?aff=YU55ECFS8AF2


七、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_KEY=sk-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 管理最好从项目早期就开始规范,而不是等到泄露、异常消耗或线上故障后再补。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐