密钥不能直接写进 config.py 或 .env 文件,因 git 误提交、docker 构建残留、ci/cd 误读等风险极易导致泄露;应通过 os.environ["key"] 强制校验存在性,或用 pydantic_settings 集中加载与类型校验,启动即失败,确保敏感信息仅存于内存且不落盘、不打印。

为什么不能直接把密钥写进 config.py 或 .env 文件?
因为一旦代码提交到 Git,密钥就可能被泄露——哪怕加了 .gitignore,也常有人误提交、本地调试时临时取消忽略、或 CI/CD 流水线自动读取了不该读的文件。更危险的是,Docker 构建时如果 COPY 了含密钥的文件,镜像层里就永久残留了明文。
用 os.getenv() + 环境变量注入是最小侵入方案
应用启动前由运维或容器平台注入环境变量,Python 运行时只读取,不硬编码。关键点不是“怎么读”,而是“怎么确保它一定存在且类型正确”:
- 别直接用
os.getenv("DB_PASSWORD"),它返回None时容易引发静默错误;改用os.environ["DB_PASSWORD"](触发KeyError)或封装校验逻辑 - 敏感字段必须设默认值为
None,禁止设空字符串或占位符(比如"changeme"),否则测试环境可能误用默认值连上生产库 - 在应用初始化早期(如
__init__.py或main.py开头)集中校验,失败立刻sys.exit(1),不留给后续模块猜测空间
import os
import sys
<p>required = ["API_KEY", "DB_URL", "JWT_SECRET"]
missing = [k for k in required if not os.environ.get(k)]
if missing:
print(f"Missing env vars: {missing}")
sys.exit(1)
</p>
CI/CD 和本地开发如何安全共享配置结构?
用 pydantic_settings(v2)或 pydantic.BaseSettings(v1)定义配置模型,它能自动从环境变量加载、类型转换、并提供清晰报错:
- 字段声明为
str但环境变量是数字?会报ValidationError,而不是默默转成字符串 - 支持
SecretStr类型,打印实例时不暴露值(repr显示**********),适合日志调试 - 避免手写
if not x: raise ValueError,把校验逻辑交给成熟库
from pydantic_settings import BaseSettings from pydantic import SecretStr <p>class Settings(BaseSettings): db_url: str api_key: SecretStr timeout_sec: int = 30</p><p>settings = Settings() # 自动从 os.environ 加载 </p>
绝对不要碰的三个操作
这些看似方便的操作实际大幅提高泄露概率:
- 在代码里调用
subprocess.run(["cat", ".env"])或类似读取文件逻辑——等于把密钥从环境变量又倒回明文上下文 - 用
python-decouple的Config默认从.env文件读取,若忘记设RepositoryEnv(None),就会静默 fallback 到当前目录下的.env,本地测试没问题,上线后可能意外读到测试密钥 - 把密钥塞进
logging.config.dictConfig的字典里再传给日志模块——某些日志处理器(如SMTPHandler)可能把整个配置 dump 到错误邮件中
最稳妥的路径始终是:环境变量 → 启动时校验 → 内存中持有 → 不落盘、不打印、不透传给下游库的配置字典。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











