1. 项目概述:为什么"不同环境"的密钥管理这么重要
前阵子在整理 Cloudflare 项目时,发现一个很容易被忽视、但一旦踩坑就特别头疼的问题:不同环境的密钥混用。很多人把开发、测试、生产环境的 Cloudflare 密钥都放在同一个配置文件里,甚至直接写死在代码中,结果开发和生产的权限边界完全模糊,等出问题的时候根本不知道是哪个环境在调用哪个权限。
这个项目其实解决的痛点很明确:在 Cloudflare 生态里,通过合理的密钥规划、环境隔离和轮换机制,让不同环境(dev/staging/prod)各用各的密钥,互不干扰,同时保证安全性和可审计性。适合正在使用 Cloudflare Pages、Workers、API 网关的开发者,或者需要维护多环境部署的运维同学参考。
我初看到这个需求时,第一反应是"不就是改几个密钥吗",但真正落地时才发现,这里面涉及 API Token 的权限粒度、Workers 的 Secrets 注入机制、wrangler 配置的 environment 区分,甚至是 CI/CD 流水线里的密钥传递链条。整个方案的设计和踩坑过程,值得单独拿出来写一篇。
这里我先把核心结论放在前面:Cloudflare 的密钥管理,本质上不是"在后台改个值"那么简单,而是要在"环境-权限-存储-轮换"四个维度上做好规划。下面我会把这四个维度逐一拆开,结合实操过程说明白。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境拆分与密钥规划:先想清楚再动手
2.1 为什么不能所有环境共用一把密钥
很多团队最开始都是图省事,开发、测试、生产统一用一个 Global API Key 或者一个宽权限的 API Token。短时间看确实省事,但时间一长就会出现几个问题:
-
权限无法收敛:开发环境可能只需要 Workers 脚本的读写权限,但因为共用密钥,这个 Token 往往带着 Account 级别的全部权限,一旦开发环境的代码被泄露,生产环境就跟着遭殃。
-
审计无从下手:Cloudflare 的日志里只会记录"哪个 Token 做了什么操作",如果所有环境都用同一个 Token,你根本分不清某个变更到底是测试环境调的还是生产环境调用的。
-
轮换成本极高:只要任何一个环境出了问题需要吊销密钥,所有环境都得跟着改,被迫中断服务。
这里有个很形象的类比:就像家里的钥匙,如果所有房间都共用一把万能钥匙,那一旦钥匙丢了,所有房间都得换锁。分环境管理密钥就是给每个房间配不同的钥匙,哪个房间出问题就只换哪把。
2.2 Cloudflare 的密钥体系:从 API Token 到 Workers Secrets
在规划之前,得先搞清楚 Cloudflare 目前有哪些密钥形式。我常用的有四类:
| 密钥类型 | 作用域 | 典型用途 | 可轮换性 |
|---|---|---|---|
| Global API Key | 账号级 | 老版 API 认证,权限极大 | 可轮换,但影响面也大 |
| API Token | 账号/区域级 | 细粒度权限控制,推荐使用 | 可独立轮换,不影响其他 Token |
| Workers Secrets | 特定 Worker 级别 | 存储环境变量型敏感数据(如数据库密码、第三方 Key) | 可在后台或通过 wrangler 更新 |
| Access Service Token | Access 应用级别 | 服务间访问 Cloudflare Access 保护资源 | 可轮换 |
我个人的建议是:Global API Key 能不用就不用。这个 Key 相当于账号的"万能钥匙",一旦泄露,攻击者可以完全接管你的 Cloudflare 账号。实际项目中绝大多数场景用 API Token + Workers Secrets 组合就足够了。
2.3 环境命名规范与权限矩阵设计
不同环境的密钥规划,建议遵循下面这套命名和权限矩阵。当然你可以根据团队情况调整,但大原则是"环境越接近生产,权限越收敛":
- 开发环境(dev):Token 命名
dev-zone-edit,权限只给某个 Zone 的 Workers Scripts 编辑权限,过期时间设短一点(比如 30 天),方便定期轮换。 - 测试/预发环境(staging):Token 命名
staging-zone-edit,权限同 dev,但可以额外给 Workers Routes 的编辑权限,方便测试灰度发布。 - 生产环境(prod):Token 命名
prod-zone-dns-read,权限只给 DNS 读取或 Workers 发布的专用权限,并且绑定固定的 IP 地址,只有跳板机或 CI 出口 IP 才能调用。
这里特别要强调一点:权限最小化不是说功能少了,而是安全边界清晰了。很多开发者在创建 Token 时,图省事直接勾选 Account > All Resources > Edit,这就是把万能钥匙给了出去。实际上 Cloudflare 的 API Token 权限配置非常细,可以精确到某个 Zone 的某项操作,多花两分钟细分权限,后续能省大量的安全排查时间。
2.4 密钥轮换周期怎么定
密钥轮换这个话题,在 Cloudflare 场景下容易被忽略,但恰恰是"不同环境密钥管理"里最核心的安全动作。我的经验是设置三个层次:
- 开发环境:30 天轮换一次,反正权限小,轮换成本低。
- 测试环境:60 天轮换一次,配合 CI/CD 流水线自动更新。
- 生产环境:30-45 天轮换一次,但要做变更窗口和回滚预案。
有一个实际案例:我之前维护的一个项目,生产环境的 API Token 一直用了大半年,直到有一次查日志发现某个来源 IP 反复尝试调用 API 才意识到可能泄露了。幸好发现得早,没有造成实际损失,但那次之后我把所有环境的 Token 都加上了过期时间。在 Cloudflare 后台创建 Token 时,有一个 "Expire" 选项,很多人直接忽略了,其实这是很好的安全兜底机制。
3. 核心实操:在 Cloudflare 中按环境配置和修改密钥
3.1 第一步:在 Cloudflare 后台创建分环境 API Token
登录 Cloudflare 后台后,点击右上角头像进入 My Profile,然后选择 API Tokens。这个入口很多人找不到,其实就在和 "Global API Key" 同一个页面里。
创建 Token 时,核心操作是 Create Token > Create Custom Token。这里我以"开发环境 Worker 编辑权限"为例,具体步骤:
- Token 名称:填
dev-worker-editor,这样在日志和审计中一眼能认出是哪个环境。 - Permissions:选择
Account > Workers Scripts > Edit,然后Zone > Workers Routes > Edit。如果你的开发环境只涉及某个测试域名,Permissions 里的 Zone 资源也要对应选具体的 Zone,不要选All Zones。 - Account Resources:选择
Include > 你的账号。 - Zone Resources:选择
Include > 特定域名,这里选开发用的域名(比如dev.example.com)。 - Client IP Address Filtering:选
Only allow from specific IP addresses,填上你公司出口 IP 或开发跳板机 IP。 - TTL 或 Expire:建议设置 30 天过期,到期后 CI 会报错提醒你轮换,反而是一个自动化的轮换触发点。
创建完 Token 后,页面会显示一次完整的 Token 字符串,记得立即保存到密码管理器或 CI 的 Secret 里,关掉页面后就再也看不到了。
注意:
Client IP Address Filtering这个功能非常实用,但很多人容易忽略。它相当于给 Token 加了网络层限制,即使 Token 泄露到公网,攻击者不在白名单 IP 范围内也无法使用。生产环境的 Token 一定建议开启这个功能。
3.2 第二步:用 wrangler 为不同环境配置 Secrets
如果你的项目使用 Cloudflare Workers,那么 wrangler.toml 是配置环境边界的核心文件。我一般这样组织:
toml复制name = "my-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"
# 默认环境(通常对应开发环境)
[env.dev]
vars = { API_BASE = "https://dev-api.example.com" }
[env.staging]
vars = { API_BASE = "https://staging-api.example.com" }
[env.prod]
vars = { API_BASE = "https://api.example.com" }
这个配置文件本身不存放敏感密钥,只存放普通配置变量。真正的密钥(比如数据库密码、第三方服务 Key)通过 wrangler secret put 命令注入。
具体操作是:先在 Cloudflare 后台为每个环境创建对应的 Worker 环境(或者用同一个 Worker 的不同 env),然后依次执行:
bash复制# 开发环境注入密钥
wrangler secret put MY_SECRET_KEY --env dev
# 测试环境注入密钥
wrangler secret put MY_SECRET_KEY --env staging
# 生产环境注入密钥
wrangler secret put MY_SECRET_KEY --env prod
执行后,wrangler 会交互式提示你输入密钥值。这样存在 Cloudflare 里的其实是 dev/staging/prod 三套独立的 Secrets,彼此隔离。
这里说明一下为什么我不用 vars 直接写密钥:vars 是明文存储在配置文件里的,任何能看到 wrangler.toml 的人都能获取密钥;而 Secrets 是加密存储,只能通过环境变量方式在 Worker 运行时读取,不会直接暴露在配置文件中。生产环境一定不要图省事把密钥写进 vars。
3.3 第三步:不同环境密钥的读取与代码分支
在 Worker 代码中,读取不同环境的密钥很简单,直接通过环境变量访问即可:
js复制export default {
async fetch(request, env, ctx) {
// env 中会自动注入当前环境对应的 Secret 和 Var
const apiBase = env.API_BASE;
const secretKey = env.MY_SECRET_KEY;
// 后续业务逻辑使用 apiBase 和 secretKey
return new Response(`API Base: ${apiBase}, Secret Key 长度: ${secretKey.length}`);
}
}
这个代码不需要做任何环境判断,因为 wrangler secret put --env prod 注入的 Secret 会在部署到 prod 环境时自动对应到 env.MY_SECRET_KEY。真正需要区分环境的地方,是在发布流程中通过 wrangler deploy --env prod 来指定使用哪个环境配置。
很多团队会问:我能不能在代码里通过 env.ENVIRONMENT === 'prod' 来判断?其实不太建议这么做。环境的差异应该由基础设施层(wrangler 配置、CI 流水线)管控,而不是散落在业务代码中。业务代码一旦感知到环境差异,就容易出现"开发环境跑得好好的,生产环境却报错"的情况——因为两个环境执行的逻辑分支已经不一样了。
3.4 第四步:结合 GitHub Actions 自动注入密钥
实际生产环境中,手动执行 wrangler secret put 一次可以,但每次轮换密钥都要手动操作,效率很低。所以通常我会把密钥注入流程集成到 CI/CD 中。
这里给出一个 GitHub Actions 的示例,假设你已经把不同环境的 API Token 存到了 GitHub 的 Repository Secrets 中:
yaml复制name: Deploy Worker
on:
push:
branches: [main]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm install
# 用 GitHub Secrets 中的 CLOUDFLARE_API_TOKEN 登录
- name: Deploy to production
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.PROD_CLOUDFLARE_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
run: |
npx wrangler deploy --env prod
echo "PROD_SECRET_KEY" | npx wrangler secret put MY_SECRET_KEY --env prod
这段流水线的关键是:CLOUDFLARE_API_TOKEN 本身也是分环境的。PROD_CLOUDFLARE_TOKEN 是生产专用 Token,权限仅限 Workers 部署;测试环境用 STAGING_CLOUDFLARE_TOKEN,开发环境用 DEV_CLOUDFLARE_TOKEN。这样即使某个环境的 Token 在 CI 日志中意外泄露,也只会影响一个环境,不会波及全部。
关于 Secret 的注入方式,还有一个细节:wrangler secret put 命令默认会从 stdin 读取密钥值,所以可以用 echo "值" | wrangler secret put ... 的方式实现非交互式注入。这在 CI 中非常关键,否则流水线会因为等待输入而卡死。
3.5 第五步:用 Cloudflare Dashboard 排查和修改已有 Secrets
如果要查看或修改某个 Worker 环境已经存在的 Secrets,不需要重新执行 wrangler 命令。在 Cloudflare Dashboard 中,进入 Workers & Pages,选择你的 Worker,点 Settings,再点 Variables and Secrets,就能看到当前环境的所有变量和密钥。
在这里可以直接:
- 查看 Var 的当前值(明文)。
- 覆盖 Secret 的值(不会显示旧值,只能重新输入)。
- 调整环境切换(页面顶部有
Production/Preview切换,注意 Preview 对应的是通过wrangler deploy --env部署的预览环境)。
一个小技巧:如果你发现某个 Secret 值无法确认是哪个环境在用的,可以在 Worker 代码中临时加一个调试接口,通过请求头或路径触发返回当前环境名称和 Secret 的最后几位,这样就能快速定位。
4. 密钥安全与轮换落地:把"改了密钥"变成"安全地改了密钥"
4.1 密钥存储选型:密钥不应该躺在配置文件里
在 Cloudflare 场景下,不同环境的密钥存储方式,我见过三种常见方案:
- GitHub / GitLab Secrets:适合与 CI/CD 集成,每次部署时动态注入,推荐使用。
- Cloudflare Workers Secrets:适合 Worker 运行时读取,和 wrangler 配合最顺滑。
- 第三方 Secrets 管理工具(如 Vault、AWS Secrets Manager):适合内容较多、需要定时自动轮换的场景,但引入成本较高,小团队不一定需要。
如果是个人项目或小团队,直接用 GitHub Secrets + Cloudflare Workers Secrets 就够了。但要注意:GitHub Secrets 里存的是 API Token,Cloudflare Workers Secrets 里存的是业务密钥,两者要区分开。我之前见过有人把 Cloudflare 的 Global API Key 直接存在 GitHub Secrets 里,然后权限还开的 Account 级 Edit,这种情况一旦 GitHub 仓库变成 public,整个 Cloudflare 账号就裸奔了。
4.2 自动轮换机制:定期改密钥不靠记忆
密钥轮换是一场跟"记忆力和惰性"的博弈。我常用的一个机制是:
- 用脚本读取当前密钥的过期时间,距离过期不足 7 天时发送告警到钉钉/邮件。
- CI 流水线中检测到生产环境的 Token 有效期少于 15 天时,自动失败并提醒人工轮换。
- 每次轮换后,立刻更新 Cloudflare Dashboard 中对应 Token 的备注和过期时间,并记录到团队的密钥台账里。
对于更自动化的方案,Cloudflare 其实支持通过 API 动态创建和删除 Token(POST /client/v4/user/tokens)。如果有开发能力,可以写一个定时任务,每次先创建新 Token、再更新 CI 环境变量、最后吊销旧 Token。但这套流程对多数团队来说过于复杂,人工轮换 + 过期提醒已经能覆盖 90% 的需求。
4.3 审计与监控:密钥状态一目了然
Cloudflare 有一个容易忽略的功能——Audit Logs。在后台的 Manage Account > Audit Logs 里面,可以查看所有通过 API Token 进行过的操作。这个日志能告诉我们:当前环境(dev 还是 prod)的密钥最后一次被调用是什么时候、从哪个 IP 发起的、执行了什么操作。
利用这个日志可以做很多有意思的事:
- 异常检测:如果一个 dev 环境的 Token 突然在深夜从非白名单 IP 调用,大概率是泄露了。
- 权限验证:修改权限矩阵后,通过日志确认每个环境只有预期的调用行为。
- 审计合规:如果你需要向客户或上级证明"我们已经对生产密钥做了严格的访问控制",Audit Logs 就是最直接的证据。
我个人的习惯是:每周五快速扫一遍 Audit Logs,重点看 prod 环境的 Token 是否有异常调用。这个操作耗时不到 5 分钟,但带来的安全感很足。
4.4 密钥泄露应急处理流程
万一真的发生密钥泄露,第一件事不是慌,而是按顺序执行下面的动作:
- 立即吊销泄露的 Token:在 Cloudflare 后台 API Tokens 页面点
Delete,或通过curl -X DELETE /client/v4/user/tokens/{id}清除。 - 检查泄露影响范围:翻一下 Audit Logs,看泄露的时间窗口内有没有异常操作。
- 轮换同环境的其他密钥:如果泄露的是生产 Token,同环境内的其他 Secrets 也要一并更新,预防横向移动。
- 更新 CI 中的 Secrets:把 GitHub Actions 或 GitLab CI 里对应的 Token 值同步替换。
- 排查泄露源:检查是代码被提交到公开仓库、日志里打印了环境变量,还是第三方服务被脱库。
这里有一个很多人容易遗漏的点:API Token 泄露后,修改了 Token,但旧 Token 如果被硬编码在某台服务器的环境变量里,那泄露源根本没有被堵住。所以应急处理必须配合"排查密钥存在于哪里"这个动作。我的做法是写一个全仓库代码搜索脚本,扫描 sk- 开头的字符串,同时检查服务器环境变量列表,确认没有遗留的旧密钥。
5. 常见问题与排查技巧实录
5.1 密钥明明设置好了,但 Worker 读出来是 undefined
这个问题在开发环境尤其常见,因为我发现不少人是直接 wrangler dev --env dev 本地调试,而 wrangler dev 默认读取的是 wrangler.toml 中的 vars,并不会自动加载 --env dev 的 Secrets。
解决方法:本地调试时需要单独通过 wrangler secret put MY_SECRET_KEY --env dev 注入,或者你可以在本地创建一个 .dev.vars 文件,wrangler 会自动加载(注意这个文件要加进 .gitignore,别提交到仓库)。
bash复制# .dev.vars 示例(仅限本地开发)
MY_SECRET_KEY=local-dev-secret-value
但 .dev.vars 只在本地开发时生效,生产环境的 Secrets 还是通过线上注入,别搞混了。
5.2 不同环境的 Secrets 互相干扰
如果你用同一个 Worker 名称部署到不同环境,并且用 wrangler tail --env prod 查看日志时发现环境变量是乱的,参考排查思路:
- 确认
wrangler.toml中是分别用[env.dev]、[env.prod]区分,而不是把 secrets 都写在顶级。 - 用
wrangler secret list --env prod查看当前生产环境实际绑定了哪些 Secrets,确认是否误把 dev 的密钥也部署到了 prod。 - 清空不必要的 Secrets:
wrangler secret delete MY_SECRET_KEY --env dev,只保留该环境真正需要的。
5.3 API Token 返回 403 Forbidden
部署到生产环境时,wrangler deploy -e prod 返回 403,这种情况大多数不是密钥写错了,而是权限不够:
- 先确认 Token 的 Permissions 是否包含
Workers Scripts > Edit。 - 确认 Token 绑定的 Zone Resource 是否包含你部署的目标域名。
- 如果开了 IP Filtering,确认当前执行部署的 IP 是否在白名单内。
- 用
npx wrangler whoami查看当前 Token 的权限和账号绑定情况,这个命令输出的信息会列出 Token 的权限列表和资源范围,排查必备。
5.4 密钥轮换时 CI 全部变红
轮换密钥后,CI 突然全部挂掉,多半是 GitHub Secrets 里的 Token 没有同步更新。这里给你一个我踩过坑后总结的流程:
- 在 Cloudflare 后台创建新 Token 后,先复制到本地临时环境变量。
- 在 GitHub 仓库的 Settings > Secrets and variables > Actions 里,找到对应的
PROD_CLOUDFLARE_TOKEN,粘贴新值。 - 然后删掉旧 Token,而不是先删旧 Token 再更新 CI,否则中间窗口期所有部署都会中断。
- 最后用
curl或 wrangler 快速验证新 Token 是否可用。
5.5 如何判断生产环境密钥是否真的"安全"
最后分享一个自查清单,每三个月做一次:
- [ ] 所有环境的 Token 是否都设置了过期时间?
- [ ] 生产环境的 Token 是否开启了 IP 白名单?
- [ ] 是否还留有 Global API Key?有的话建议吊销,改用 API Token。
- [ ] Git 仓库的历史提交里有没有出现过密钥字符串?用 git log 搜一下。
- [ ] CI/CD 日志中是否打印过
var.MY_SECRET_KEY或env.MY_SECRET_KEY?如果有,立即清理日志输出。
6. 一点收尾的经验之谈
做 Cloudflare 密钥管理这么久,我最大的感受是:密钥管理这件事,枯燥但很重要,它不像功能开发那样有即时反馈,但一旦出问题就是大事故。很多人觉得"反正数据都是公网的,密钥泄露了也没啥",这个想法非常危险。
有几个小经验分享给大家:
- 不同环境的密钥,一定要在命名和权限上做出区分。
dev-、staging-、prod-前缀虽简单,但能让所有操作日志、CI 流水线、团队成员一眼就看明白。 - 轮换密钥时,别只改一处。Cloudflare 后台的 Token、GitHub Secrets、服务器环境变量、
.dev.vars,这些地方都可能存有副本,漏掉一个就是隐患。 - 如果团队里有超过两个人维护基础设施,建议写一份简短的"密钥管理操作手册",把创建、轮换、吊销的流程固化下来,避免每个人按自己习惯操作。
我个人在维护的几个项目里,把这套分环境密钥体系跑了大半年,最大的收益不是"更安全"这个抽象概念,而是排查问题变得非常快。任何一次 API 调用异常,直接查对应环境的 Audit Logs,立刻能定位到是哪个 Token、哪个权限出了问题,不用再像以前一样把所有环境的配置翻个底朝天。
这个方案后续还可以继续扩展,比如对接 Vault 实现全自动轮换、为每个环境单独配置通知告警等。但先把当前环境下的密钥管理做到"井水不犯河水",就已经能解决大多数团队 80% 的安全和管理问题了。
