API密钥安全管理与环境配置
API 密钥(Key)是访问 LLM 服务的凭证,就像银行卡密码,泄露后果严重:攻击者可以用你的密钥消耗你的配额,产生大量费用,甚至访问你的账户数据。密钥安全管理是 AI 应用工程化的第一道防线。
API 密钥安全管理与环境配置
API 密钥(Key)是访问 LLM 服务的凭证,就像银行卡密码,泄露后果严重:攻击者可以用你的密钥消耗你的配额,产生大量费用,甚至访问你的账户数据。密钥安全管理是 AI 应用工程化的第一道防线。
为什么需要这一篇:新手最常犯的错误是把 API Key 硬编码在代码里,然后提交到 GitHub。GitHub 会自动扫描公开仓库中的密钥并通知,但通知到来时可能已经产生了费用。这篇从根本上解决这个问题。
最常见的错误:密钥硬编码
从环境变量到密钥管理服务的安全访问架构
# ❌ 绝对不要这样做
from openai import OpenAI
client = OpenAI(base_url="https://api.deepseek.com", api_key="sk-真实密钥")
# ❌ 不要提交到 git
# 即使是私有仓库,团队成员都能看到,代码泄露时密钥也会泄露
硬编码(即直接把密钥写死在代码文件里,而不是从外部配置读取)的密钥一旦提交到 git(代码版本管理工具,用来追踪代码历史变化,支持多人协作),即使后来删除,在历史记录里仍然存在。GitHub 等平台会扫描公开仓库中的密钥,一旦发现会立即通知,但损失可能已经发生。
基础方案:环境变量
import os
from openai import OpenAI
# 从环境变量读取密钥
api_key = os.environ.get("DEEPSEEK_API_KEY")
if not api_key:
raise ValueError("DEEPSEEK_API_KEY 环境变量未设置")
client = OpenAI(
base_url="https://api.deepseek.com",
api_key=api_key
)
在终端设置环境变量(临时,关闭终端后失效):
export DEEPSEEK_API_KEY=sk-你的key
export OPENAI_API_KEY=sk-你的key
开发环境:.env 文件
在项目根目录创建 .env 文件:
# .env
DEEPSEEK_API_KEY=sk-你的key
OPENAI_API_KEY=sk-你的key
DATABASE_URL=postgresql://localhost/mydb
DEBUG=true
关键:把 .env 加入 .gitignore:
# .gitignore
.env
.env.local
.env.*.local
*.env
在代码中加载 .env:
from dotenv import load_dotenv
import os
load_dotenv() # 加载 .env 文件到环境变量
api_key = os.environ["DEEPSEEK_API_KEY"]
安装 python-dotenv:
pip install python-dotenv
# 或
uv add python-dotenv
不同环境的配置管理
实际项目通常有多个环境:
项目根目录/
├── .env # 本地开发(不提交到 git)
├── .env.example # 示例文件(提交到 git,不含真实值)
├── .env.test # 测试环境配置(可能提交,不含密钥)
└── .gitignore # 确保 .env 不被提交
.env.example 文件(提交到 git,作为文档):
# .env.example - 复制为 .env 并填入真实值
DEEPSEEK_API_KEY=your_deepseek_api_key_here
OPENAI_API_KEY=your_openai_api_key_here
DATABASE_URL=postgresql://localhost/your_db_name
DEBUG=false
加载不同环境的配置:
from dotenv import load_dotenv
import os
env = os.environ.get("APP_ENV", "development")
if env == "test":
load_dotenv(".env.test")
else:
load_dotenv() # 默认加载 .env
生产环境:不用 .env 文件
生产服务器上不应该有 .env 文件,密钥应该通过以下方式注入:
Docker/容器部署(Docker 是一种将应用及其依赖打包成"容器"的技术,方便在不同服务器上一致地运行):
# docker run 时传入环境变量
docker run -e DEEPSEEK_API_KEY=xxx -e OPENAI_API_KEY=yyy my-app
# docker-compose.yml
services:
app:
environment:
- DEEPSEEK_API_KEY=${DEEPSEEK_API_KEY} # 从宿主机环境读取
云平台(以常见平台为例):
# Heroku
heroku config:set DEEPSEEK_API_KEY=xxx
# Railway
railway variables set DEEPSEEK_API_KEY=xxx
# Vercel
vercel env add DEEPSEEK_API_KEY
Kubernetes Secret(Kubernetes 是一种容器编排平台,用于管理大规模服务器上的应用程序;Secret 是它存储敏感配置的专用对象):
apiVersion: v1
kind: Secret
metadata:
name: api-keys
type: Opaque
data:
deepseek-api-key: base64编码的密钥
使用 pydantic-settings 管理配置
对于稍复杂的项目,推荐用 pydantic-settings(一个 Python 库,在 pydantic 数据验证功能的基础上专门增强了配置管理能力,能自动从环境变量读取配置并做类型校验)统一管理配置:
from pydantic_settings import BaseSettings
from pydantic import SecretStr
class Settings(BaseSettings):
# SecretStr 类型:打印时会显示 ***** 而不是真实值
deepseek_api_key: SecretStr
openai_api_key: SecretStr | None = None
# 普通配置项
model: str = "deepseek-chat"
max_tokens: int = 1024
debug: bool = False
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
# 单例模式
settings = Settings()
# 使用
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.deepseek.com",
api_key=settings.deepseek_api_key.get_secret_value()
)
pydantic-settings 的优势:
- 自动从环境变量和
.env文件读取 - 类型验证(配置错误时立即报错,而不是运行时才发现)
SecretStr防止密钥被意外打印到日志
密钥轮换
定期更换 API 密钥是安全最佳实践:
- 在控制台创建新密钥
- 更新所有使用该密钥的服务配置
- 验证新密钥可用
- 删除旧密钥
对于高安全要求的场景,可以设置密钥过期时间(部分平台支持)。
检测密钥是否泄露
如果怀疑密钥泄露:
- 立即撤销:在 API 控制台删除该密钥
- 检查用量:查看是否有异常的 API 调用记录
- 审查代码:检查密钥是否出现在代码、日志、错误消息中
- 扫描 git 历史:使用
git log -p | grep "sk-"检查是否有密钥提交记录
工具推荐:
git-secrets:在 commit 前扫描,阻止密钥提交truffleHog:扫描 git 历史中的密钥- GitHub 的 Secret Scanning:自动扫描公开仓库
最小权限原则
如果 API 平台支持,为不同环境创建不同的密钥,并设置最小权限:
- 开发密钥:低限额,仅用于开发
- 测试密钥:中等限额,仅用于 CI/CD(持续集成/持续交付,自动化测试和部署代码的流程)
- 生产密钥:根据业务需求设置限额,定期轮换
这样即使某个环境的密钥泄露,损失也是可控的。
常见错误和解决方法
| 错误 | 后果 | 解决方法 |
|---|---|---|
| 密钥硬编码在代码里 | 提交到 git 后泄露 | 改用环境变量或 .env 文件 |
| .env 文件提交到 git | 密钥泄露给所有有仓库权限的人 | 把 .env 加入 .gitignore |
| 所有环境用同一个密钥 | 一个环境泄露影响全部 | 按环境创建不同密钥,设置限额 |
| 密钥直接写在日志里 | 日志系统泄露密钥 | 用 SecretStr 类型防止打印 |
小结:密钥安全 checklist
- 密钥存放在环境变量或 .env 文件中,不在代码里
- .env 文件已加入 .gitignore
- 项目提供了 .env.example(不含真实值)
- 生产环境通过平台环境变量注入,不使用 .env 文件
- 开发/测试/生产分别使用不同密钥
- 定期检查 git 历史中是否有密钥泄露(
git log -p | grep "sk-")