如何在代码中安全管理API密钥和机密
你提交代码,推送到GitHub,然后继续工作。几天后,你发现你的API密钥从公共仓库中被抓取,并用于累积数千美元的云账单。这不是罕见的边缘情况;这是现代开发中最常见且代价高昂的安全错误之一。源代码中的硬编码机密是给攻击者的礼物,而且避免它们出奇地容易。
为什么硬编码机密如此危险
当你直接在代码中嵌入API密钥、数据库密码或私有令牌时,你就失去了对谁可以看到它的控制。源代码会传播:它被克隆、分叉、复制到Docker镜像中、粘贴到聊天应用中,有时还会被意外发布。一旦机密进入版本控制,它就会永远存在于Git历史中,即使你在后续提交中删除了它。
攻击者会主动扫描公共仓库中看起来像密钥的模式。自动化机器人可以在几分钟内找到并利用泄露的密钥。即使在私有仓库中,硬编码机密也违反了最小权限原则:每个具有读取权限的开发人员自动获得生产凭证。
规则#1:永远不要硬编码机密
这听起来很明显,但它是基础。第一步是从源文件中移除任何机密。这不仅包括像sk_live_...这样的明显字符串,还包括连接字符串、私钥和webhook签名机密。
相反,你的代码应该在运行时从环境中读取机密。以下是一个简单的Python示例:
import os
api_key = os.environ.get("PAYMENT_API_KEY")
if not api_key:
raise RuntimeError("PAYMENT_API_KEY is not set")
在Node.js中,你会使用process.env.PAYMENT_API_KEY。在Go中,使用os.Getenv("PAYMENT_API_KEY")。模式是通用的:代码期望机密从外部提供。
安全使用环境变量
环境变量是一个很大的改进,但它们不是银弹。它们可能通过错误报告、调试日志或进程列表泄露。遵循以下实践:
- 永远不要记录环境变量。避免在错误处理程序中转储
process.env或os.environ。 - 仅将
.env文件用于本地开发。从第一天起就将.env添加到.gitignore。 - 提供一个
.env.example文件,其中包含占位符值,以便新开发人员知道要设置什么。 - 在启动时验证必需的机密。如果缺少密钥,则快速失败,而不是在生产环境中稍后崩溃。
对于本地开发,像python-dotenv或Node.js的dotenv这样的库可以轻松加载.env文件,而无需硬编码任何内容。只需记住:该文件绝不能提交。
用于生产的集中式机密管理器
环境变量适用于小型项目,但当你拥有许多服务、多个环境以及审计需求时,它们会变得难以管理。专用的机密管理器通过加密存储机密、使用细粒度策略控制访问以及提供审计跟踪来解决这些问题。
流行的选项包括:
- HashiCorp Vault – 自托管,高度灵活,支持动态机密。
- AWS Secrets Manager – 与AWS服务原生集成,自动轮换。
- Google Secret Manager – 适用于GCP的类似服务。
- Azure Key Vault – 适用于Microsoft Azure环境。
- Doppler、Infisical或1Password Secrets Automation – 跨平台SaaS选项。
你的应用程序在启动时或按需从管理器获取机密,通常使用SDK。这将机密存储与代码解耦,并允许你在不重新部署的情况下轮换凭证。
CI/CD管道中的机密
你的构建和部署管道也需要机密,例如注册表密码或部署令牌。大多数CI系统(GitHub Actions、GitLab CI、CircleCI)提供加密的机密存储。使用这些功能,而不是将机密放在管道配置文件中。
例如,在GitHub Actions中,你在仓库设置中定义机密,并像这样引用它们:
steps:
- name: Deploy
run: ./deploy.sh
env:
API_TOKEN: ${{ secrets.DEPLOY_API_TOKEN }}
小心来自分叉的拉取请求:默认情况下,机密不会传递给由分叉PR触发的工作流,这是好的。永远不要在日志中回显机密;如果你的CI系统支持,请屏蔽它们。
定期轮换机密
即使存储完美,机密也可能通过其他渠道泄露:被入侵的笔记本电脑、配置错误的日志服务或离职员工。轮换限制了暴露窗口。
根据敏感性设置轮换计划。高价值密钥(支付网关、管理API)可能每30-90天轮换一次。较低风险的密钥可以较少轮换。尽可能自动化轮换:AWS Secrets Manager和Vault可以自动轮换数据库凭证。
轮换时,确保你的应用程序在过渡期间能够处理多个有效机密。一个常见的模式是在短时间内接受旧机密和新机密,然后停用旧机密。
检测和防止泄露
预防胜于治疗,但检测是你的安全网。使用预提交钩子在提交之前扫描机密。像git-secrets、trufflehog或gitleaks这样的工具可以捕获意外提交。
还要在Git托管平台(GitHub、GitLab、Bitbucket都提供此功能)上启用机密扫描。如果机密确实泄露了,立即撤销并轮换。删除提交是不够的;一旦机密触及远程仓库,就假设它已被泄露。
机密存储方法比较
| 方法 | 最适合 | 风险 |
|---|---|---|
| 环境变量 | 小型应用、本地开发 | 通过日志、进程检查泄露 |
.env文件 |
本地开发 | 意外提交、无加密 |
| 机密管理器 | 生产、团队 | 复杂性、增加依赖 |
| CI/CD机密存储 | 构建和部署管道 | 仅限于管道范围 |
常见问题
我可以将机密存储在私有仓库中吗?
不可以。私有仓库仍然有许多具有读取权限的用户和集成。机密可能通过分叉、CI日志或被入侵的账户泄露。始终使用环境变量或机密管理器,即使对于私有代码也是如此。
如果我不小心提交了机密,该怎么办?
立即撤销并轮换机密。仅仅删除提交或重写历史是不够的,因为机密可能已经被缓存或克隆。将其视为已泄露并替换它。
环境变量对生产环境足够安全吗?
它们比硬编码更好,但对于大规模生产来说并不理想。环境变量可能在崩溃转储、调试端点或进程列表中暴露。对于生产环境,使用具有访问控制和审计的专用机密管理器。
当你需要快速格式化或验证包含非敏感设置的JSON配置文件时,JSON Formatter可以帮助你在它们破坏部署之前发现语法错误。记住:永远不要将实际的机密粘贴到在线工具中。