不要加密配置文件里的密码,而要让密码根本不出现在配置文件里;应通过外部可信服务(如vault、k8s secrets)动态注入凭据,实现运行时获取、内存驻留、无磁盘残留。

直接结论:不要加密配置文件里的密码,而要让密码根本不出现在配置文件里。 加密只是把明文变密文,但密钥总得存 somewhere —— 一旦密钥和加密数据落在同一台机器、同一个部署包或同一个 Git 仓库里,攻击者拿到二者就能解密。真正安全的做法是“凭据分离”:密码由外部可信服务动态提供,Python 只负责按需读取。
为什么 .env 文件加 AES 密码不是好方案
很多人用 python-dotenv 配合自写 AES 加密,把 DB_PASSWORD 存成 encrypted:xxxxxx。这看似安全,实则埋了三个坑:
- 密钥往往硬编码在代码里(比如
key = b"32-byte-secret-key-here"),等于把保险箱钥匙焊死在箱子上 - 解密逻辑必须随应用一起部署,任何能执行 Python 的人(包括恶意依赖、调试 shell)都能调用
decrypt()函数还原密码 -
.env文件权限设为600并不能阻止容器内进程读取/proc/1/environ—— Docker 容器里os.environ全局可见
开发环境该用 python-decouple + 模板校验
目标不是防黑客,而是防手滑提交。用 python-decouple 强制区分“有值”和“缺失”,配合 .env.example 模板防止漏配:
pip install python-decouple
项目根目录放 .env.example:
DB_HOST=localhost DB_PORT=5432 DB_USER=dev_user DB_PASSWORD=your_dev_password_here # ← 提示这是占位符,实际不提交 DEBUG=True
再写个 pre-commit hook(用 git-secrets 或自定义脚本),扫描所有 .env* 文件是否含 DB_PASSWORD= 这种明文模式。如果检测到,直接阻断 commit。
生产环境必须走 Vault 或 K8s Secrets
本地跑得通不等于线上安全。生产环境的密码必须满足“运行时注入、内存中只存活、无磁盘残留”:
- Kubernetes:用
Secret挂载为 volume,Python 通过open('/mnt/secrets/db_password', 'r').read().strip()读取 —— 文件权限设为400,且挂载为readOnly: true - 云环境(AWS/Azure/GCP):优先用原生服务(
AWS Secrets Manager、Azure Key Vault),用 SDK 按需拉取,绝不缓存到变量里 - 自建集群:用
HashiCorp Vaultagent sidecar,Python 调用http://localhost:8200/v1/secrets/data/db/prod,Vault 自动处理 token 刷新和 TLS
注意:SQLAlchemy 的 create_engine 不接受回调函数,所以不能“边连边取密码”。必须在调用前完成凭据获取,并构造完整 URL —— 但这个 URL 仅存在于内存,绝不能 print(engine.url) 或打日志。
最容易被忽略的一点:连接池生命周期
很多人以为“密码只在启动时读一次就完了”,其实不然。如果用 sqlalchemy.pool.QueuePool(默认),连接复用时不会重新鉴权,但连接断开重连时,creator 函数会被再次调用 —— 如果你在 creator 里写 os.environ.get('DB_PASSWORD'),而这时环境变量已被清空或篡改,就会静默失败。正确做法是把凭据读取逻辑封装进 creator,并加一层缓存(如 functools.lru_cache),确保每次重连都走同一套受控路径。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











