API KeyGitHub密钥安全环境变量.envSecret ScanningDevSecOps

如何安全保存 API Key,避免意外上传到 GitHub

API Key 应该放在哪里?本文介绍环境变量、.env 与 .gitignore、Secret Manager、CI/CD Secrets、最小权限,以及密钥误传 GitHub 后的撤销和历史清理流程。

如何安全保存 API Key,避免意外上传到 GitHub

API Key 从本地开发到 CI 和生产环境的安全保存路径

API Key 泄露往往不是因为复杂攻击,而是因为一个很普通的操作:

git add .
git commit -m "update config"
git push

随后才发现 .env、测试脚本、Notebook、截图或日志里包含真实密钥。

正确的原则不是“把 Key 藏得更深”,而是:

凭据与代码分离,按环境注入,使用最小权限,并假设任何进入 Git 历史的密钥都已经泄露。

本文覆盖本地开发、团队协作、GitHub Actions 和生产环境,并说明密钥误传后应该先做什么。

哪些内容属于 Secret

API Key、Token、私钥和连接串的密钥分类

常见 Secret 不只有 API Key:

  • AI API Key;
  • GitHub Personal Access Token;
  • 云平台访问密钥;
  • OAuth Client Secret;
  • 数据库连接串和密码;
  • SSH 私钥、TLS 私钥;
  • Webhook Secret;
  • Session Secret、JWT 签名密钥;
  • 服务账号 JSON;
  • 包管理器发布 Token;
  • 带签名或长期访问权限的 URL。

以下信息虽然可能不是完整 Secret,也应避免公开:

  • Key 前后缀组合;
  • 内部 Base URL;
  • 客户账号、项目 ID 和请求数据;
  • 生产日志中的 Authorization Header;
  • 可用于重置凭据的恢复码。

API Key 应该放在哪里

场景推荐方式不推荐方式
本地开发环境变量、本地 .env、系统 Keychain 或密码管理器写进源代码
团队共享Secret Manager 或受控密码库聊天、邮件、共享文档明文发送
GitHub ActionsRepository / Environment / Organization Secrets写入 Workflow YAML
云端生产平台 Secret、KMS、Vault、运行时绑定构建进前端 Bundle 或镜像层
临时测试短期、低权限、可撤销 Token复用生产管理员 Key

配置会随部署环境变化,应与代码分离。环境变量是常见接口,但真正的安全性仍取决于它的来源、进程权限、日志和生命周期。

本地开发:.env 可以用,但必须正确管理

1. 先把 .env 加入 .gitignore

# Local secrets
.env
.env.*
!.env.example

# Private keys and provider credentials
*.pem
*.key
secrets/
credentials.json

这段规则保留 .env.example,但忽略真实环境文件。

2. 只在 .env.example 中保留占位符

NBILITY_API_KEY=YOUR_API_KEY
NBILITY_BASE_URL=https://api.nbility.ai/v1
NBILITY_MODEL=YOUR_MODEL_ID

不要把“暂时可用的测试 Key”放进模板。公开仓库中的测试 Key 仍会被抓取和滥用。

3. 确认文件从未被 Git 跟踪

.gitignore 只影响未跟踪文件。若 .env 已经进入索引,后来添加忽略规则不会让它自动消失。

检查:

git status --short
git check-ignore -v .env

若文件已被跟踪,需要在撤销密钥后停止跟踪:

git rm --cached .env

然后提交删除记录。注意:这不会删除旧提交里的内容。

4. 设置合理的本地文件权限

在 Linux/macOS 中,可限制只有当前用户读取:

chmod 600 .env

权限不能防止所有泄露,但能降低同一机器上其他用户误读的风险。

在代码中读取环境变量

Node.js:

const apiKey = process.env.NBILITY_API_KEY;

if (!apiKey) {
  throw new Error('NBILITY_API_KEY is not configured');
}

Python:

import os

api_key = os.environ.get("NBILITY_API_KEY")
if not api_key:
    raise RuntimeError("NBILITY_API_KEY is not configured")

示例和错误日志都不应打印完整 Key:

// 不要这样做
console.log(process.env.NBILITY_API_KEY);

只记录变量是否存在,或者使用不会暴露可复原信息的内部凭据标识。

前端环境变量不是 Secret

这是最容易误解的地方之一。

凡是发送到浏览器的值,用户都能通过以下方式读取:

  • JavaScript Bundle;
  • DevTools Network;
  • Source Map;
  • 页面源码;
  • 浏览器扩展;
  • 代理和日志。

VITE_NEXT_PUBLIC_REACT_APP_ 等前缀通常表示变量会被嵌入客户端代码。它们适合公开配置,不适合 API Key。

正确架构是:

Browser
   ↓ authenticated request
Your backend / serverless function
   ↓ server-side secret
AI API

后端还应验证用户身份、限制模型和额度,并避免成为任何人都能调用的开放代理。

GitHub Actions 如何使用 Secrets

开发者、GitHub Actions 与生产 Secret 的隔离边界

在仓库的 Settings → Secrets and variables → Actions 中创建 Secret,再在 Workflow 中引用:

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run integration test
        env:
          NBILITY_API_KEY: ${{ secrets.NBILITY_API_KEY }}
        run: npm run test:integration

安全要点:

  • 优先使用 Environment Secrets 控制生产部署;
  • 为生产环境设置审批和分支保护;
  • 不要在 runecho Secret;
  • 不把 Secret 拼进命令行参数、构建产物或缓存键;
  • 审查第三方 Action,固定可信版本或 Commit;
  • Fork PR 默认不应获得敏感 Secret;
  • 用短期 OIDC 凭据替代长期云平台密钥(平台支持时);
  • 对不同仓库、环境和任务使用不同凭据。

GitHub 对日志中的已知 Secret 会进行遮罩,但遮罩不是万能的。编码、切片、转换后的值,或 GitHub 不认识的凭据格式,仍可能泄露。

生产环境:优先使用 Secret Manager

生产系统应从运行平台注入 Secret,而不是从仓库复制 .env

  • Cloudflare Workers Secrets;
  • AWS Secrets Manager / Parameter Store;
  • Google Secret Manager;
  • Azure Key Vault;
  • HashiCorp Vault;
  • Kubernetes Secrets 配合加密和 RBAC;
  • 平台提供的加密环境变量。

理想流程是:

CI 获得短期部署身份
→ 部署应用,但不读取生产 Secret 明文
→ 运行时按权限获得 Secret
→ 应用调用受限资源

Secret Manager 不是“保存一次就结束”。仍需配置访问控制、审计、轮换、版本和撤销流程。

最小权限和密钥分层

不要让一个 Key 同时拥有所有模型、无限额度和管理员权限。

建议按以下维度拆分:

开发 / 测试 / 生产
个人 / CI / 后端服务
只读 / 推理调用 / 管理操作
低预算 / 高预算
具体模型或项目范围

对于 AI API,至少考虑:

  • 限制可用模型;
  • 设置额度或消费上限;
  • 限制来源 IP(服务支持时);
  • 为 Agent 创建独立 Key;
  • 定期轮换;
  • 长期不用立即撤销;
  • 对异常用量设置告警。

最小权限不能阻止泄露,但能降低泄露后的损失范围。

如何在提交前阻止 Secret

启用 GitHub Secret Scanning 与 Push Protection

GitHub Secret Scanning 会扫描 Git 历史中的已知凭据类型,并可能在发现泄露后生成告警。Push Protection 可以在推送阶段阻止支持类型的 Secret 进入仓库。

但它们不是完整替代:

  • 自定义格式可能无法识别;
  • 测试数据可能产生误报;
  • 编码或拆分后的 Secret 可能漏过;
  • 私有仓库也不是安全保存凭据的地方。

在本地和 CI 中增加扫描

可以选择 Gitleaks、TruffleHog 等工具,并结合:

  • pre-commit Hook;
  • pre-push Hook;
  • CI 检查;
  • 自定义正则和允许列表;
  • 阻止高置信度泄露的合并策略。

不要把扫描器输出中的完整匹配值上传到公开日志。

提交前人工检查

git status --short
git diff --cached --stat
git diff --cached

对于二进制文件、Notebook、截图、压缩包和生成日志,还应确认它们没有嵌入凭据或敏感元数据。

API Key 已经推到 GitHub,应该怎么办

API Key 泄露后的撤销、轮换、审计和历史清理顺序

处理顺序非常重要。

第一步:立即撤销或轮换

不要先花几个小时清理 Git 历史。GitHub 官方建议先撤销或轮换凭据,因为即使稍后改写历史:

  • 已有人克隆仓库;
  • Fork、缓存或日志中可能仍有副本;
  • 自动扫描器可能已经发现;
  • PR、Issue、Action Artifact 可能保留内容。

旧 Key 撤销后,再创建新的、权限更小的 Key,并更新合法使用方。

第二步:检查滥用与影响范围

查看:

  • Provider 调用日志;
  • 余额和异常消费;
  • 新建资源或权限变更;
  • 访问 IP、时间和模型;
  • CI、部署与生产日志;
  • 该 Key 是否复用于其他系统。

必要时扩大撤销范围、暂停相关服务并通知安全负责人。

第三步:从当前代码中删除

改用环境变量或 Secret Manager,并增加 .gitignore、扫描和测试,防止再次提交。

第四步:评估是否需要改写 Git 历史

对于密码或 Token,撤销通常已经让旧值失效。历史改写主要用于减少继续传播、满足合规或删除无法撤销的敏感数据。

GitHub 推荐使用 git-filter-repo,但历史改写有明显副作用:

  • 所有后续 Commit SHA 改变;
  • 已签名提交和 Tag 可能失效;
  • PR Diff 和评论可能受影响;
  • 需要临时处理分支保护;
  • 协作者旧 Clone 可能重新污染仓库;
  • Fork 和其他副本不会自动清理。

执行前应协调所有协作者、关闭或处理开放 PR、备份并制定重新克隆方案。不要在不了解影响时直接强推。

第五步:处理 GitHub 上的其他副本

历史重写后,仍可能需要联系 GitHub Support 处理缓存视图或敏感引用,并协调 Fork 所有者。具体流程应遵循 GitHub 当前“Removing sensitive data”文档。

常见错误认识

“仓库是 Private,所以可以放 Key”

私有仓库仍可能被错误授权、账号入侵、CI 日志、第三方 Action、Fork 或内部人员暴露。Secret 应继续与代码分离。

“删除那一行再 Commit 就安全了”

旧值仍在 Git 历史。先撤销,再评估历史清理。

“GitHub 会自动遮罩所有 Secret”

只对已登记或识别的值有效,变形后的 Secret 可能出现在日志中。

“Base64 后就不是 Secret”

Base64 是编码,不是加密,任何人都能恢复。

“环境变量绝对安全”

环境变量仍可能被子进程、调试工具、崩溃报告和错误日志读取。它是配置接口,不是自动安全边界。

“一个团队共用一个管理员 Key 最方便”

这会失去归属、撤销和审计能力,并扩大泄露影响。应按人、服务和环境拆分。

Nbility API Key 的安全建议

Nbility 控制台为不同用途创建独立 Token,并根据业务设置模型范围、额度和有效期。OpenAI 兼容客户端通常通过环境变量读取:

export NBILITY_API_KEY="YOUR_API_KEY"

调用地址使用:

https://api.nbility.ai/v1

不要把真实 Token 放入:

  • 博客和文档;
  • Git 仓库;
  • AI Agent 的 Prompt 或长期记忆;
  • 工单截图;
  • 终端录屏;
  • 前端变量;
  • Dockerfile 或镜像层。

如果你正在配置编程助手,可结合 Codex CLI 接入 OpenAI 兼容 APICline 自定义 API 的权限控制 一起检查:客户端能读取 Key,不代表 Agent 应该获得生产环境全部权限。

怀疑泄露时,立即在控制台撤销旧 Token,再通过 Nbility 工单提交脱敏后的时间、Token 标识和异常调用信息。

上线前检查清单

[ ] 源代码和配置模板中没有真实 Secret
[ ] .env 和私钥文件已被 .gitignore 忽略
[ ] 已确认敏感文件从未被 Git 跟踪
[ ] 前端 Bundle 中没有服务端密钥
[ ] CI 使用 GitHub Secrets 或短期身份
[ ] 生产环境使用 Secret Manager / 平台 Secret
[ ] 每个环境和服务使用独立、最小权限凭据
[ ] Secret Scanning、Push Protection 和 CI 扫描已启用
[ ] 日志、截图、Notebook、Artifact 和缓存已检查
[ ] 已定义撤销、轮换和事件响应流程

FAQ

.env 文件安全吗?

它适合本地开发,但必须被忽略、限制权限且不共享。生产环境更适合平台 Secret 或 Secret Manager。

API Key 误传到私有 GitHub 仓库也要撤销吗?

要。无法证明无人读取时,应按泄露处理。私有可见性不等于 Secret 存储。

删除 Commit 能让 Key 恢复安全吗?

不能。密钥可能已被克隆、缓存或扫描。必须先撤销或轮换。

GitHub Secret Scanning 能发现所有 Key 吗?

不能。它覆盖许多已知格式,也支持自定义模式,但仍可能有漏报。应结合 Push Protection、本地扫描和人工审查。

可以把 Key 加密后提交吗?

只有在密钥管理、解密权限、轮换和审计都设计完善时才考虑加密配置方案。不要把解密密钥与密文放在同一仓库。

总结

安全保存 API Key 的核心流程是:

  1. 源代码只读取环境变量,不包含真实凭据;
  2. 本地 .env 被忽略,模板只保留占位符;
  3. CI/CD 使用受控 Secret 或短期身份;
  4. 生产环境使用 Secret Manager 和最小权限;
  5. 提交前使用 Push Protection、扫描器和人工检查;
  6. 一旦进入 Git,立即撤销和轮换,再处理历史与影响范围。

密钥安全不是一个 .gitignore 文件,而是从创建、分发、使用、监控到撤销的完整生命周期。

参考资料

相关文章

用 Nbility 跑通你的 Agent 工作流

获取 API Key,统一接入 OpenAI 兼容模型和开发工具。

管理 API Key