4、环境变量与安全:敏感信息泄露风险、.env文件的最佳实践、python-dotenv库的使用
说到环境变量,很多新手第一反应是「这玩意儿不就是存配置的吗?」
嗯,没错。但我要告诉你一个残酷的事实——环境变量用不好,就是给黑客留后门。
我自己在早期做项目时,就犯过一个低级错误:把数据库密码直接写死在代码里,还顺手提交到了 GitHub 公开仓库。结果呢?第二天服务器就被挖矿程序入侵了。那次教训让我彻底明白了——敏感信息管理,是 DevOps 的底线。
4.1 敏感信息泄露的常见场景
先看看哪些地方最容易翻车:
| 泄露场景 | 典型表现 | 风险等级 |
|---|---|---|
| 硬编码在源码中 | 代码里直接写 password = "123456" |
极高 |
| 配置文件提交到 Git | .env、config.json 被 git add 并 push |
高 |
| 日志中打印敏感值 | 启动时输出 DB_PASSWORD=xxx |
中 |
| 容器镜像层残留 | Dockerfile 中 ENV 指令未清理 |
中 |
| 错误页面暴露环境变量 | Flask/Django debug 模式返回 environ |
高 |
settings.py 里,然后整个仓库是公开的。三天后,他们的 S3 存储桶被清空了。这不是段子,是真事。
4.2 .env 文件的最佳实践
.env 文件是管理环境变量的标准方式。但很多人只是「用了」,并没有「用好」。
我个人习惯遵循以下几条铁律:
4.2.1 永远不要提交 .env 到版本控制
这是最基本的一条。在项目根目录创建 .gitignore,加上:
# .gitignore
.env
.env.local
.env.production
*.env
你想想看,如果 .env 进了 Git,那跟把密码贴在公司门口有什么区别?
4.2.2 提供 .env.example 作为模板
为了让团队成员知道需要哪些变量,我会在仓库里放一个 .env.example:
# .env.example
DB_HOST=localhost
DB_PORT=5432
DB_USER=your_username
DB_PASSWORD= # 留空,不填真实值
SECRET_KEY=change_me_in_production
.env.example 中,敏感字段留空或填占位符,并加上注释说明。这样新人拿到项目就知道该问谁要这些值。
4.2.3 按环境拆分 .env 文件
我建议按环境维护不同的文件:
| 文件名 | 用途 | 是否提交 |
|---|---|---|
.env |
本地开发环境 | ❌ 不提交 |
.env.example |
变量模板 | ✅ 提交 |
.env.production |
生产环境(仅服务器保留) | ❌ 不提交 |
.env.test |
测试环境 | ⚠️ 可选提交(无敏感值) |
4.2.4 变量命名规范
命名要清晰、一致。我一般用全大写 + 下划线:
# 好的命名
DATABASE_URL=postgresql://user:pass@localhost:5432/mydb
REDIS_HOST=127.0.0.1
LOG_LEVEL=DEBUG
# 差的命名
dburl=xxx
host=xxx
lvl=debug
说白了,命名规范不是为了好看,是为了让运维同事少骂你两句。
4.3 python-dotenv 库的使用
Python 项目里,python-dotenv 是我必装的库。它能把 .env 文件里的变量加载到 os.environ 中,简单又安全。
4.3.1 安装
pip install python-dotenv
4.3.2 基本用法
在项目入口文件(比如 app.py 或 main.py)最顶部加载:
from dotenv import load_dotenv
import os
# 加载 .env 文件(默认在当前目录查找)
load_dotenv()
# 读取环境变量
db_host = os.getenv("DB_HOST", "localhost")
db_port = os.getenv("DB_PORT", "5432")
db_password = os.getenv("DB_PASSWORD")
if not db_password:
raise ValueError("DB_PASSWORD 未设置!")
os.getenv() 的第二个参数是默认值。但敏感信息不要给默认值,宁可让程序启动失败,也不能用弱密码兜底。
4.3.3 指定 .env 文件路径
有时候 .env 不在当前目录,或者你想加载不同环境的文件:
# 加载指定路径的 .env 文件
load_dotenv(dotenv_path="/etc/myapp/.env.production")
# 加载多个 .env 文件(后面的覆盖前面的)
load_dotenv(".env.default")
load_dotenv(".env.local", override=True) # override=True 允许覆盖已有变量
我在项目中经常这样用:先加载公共配置,再加载本地覆盖配置。这样团队共享一套基础变量,每个人又能有自己的本地调试值。
4.3.4 在 Django/Flask 中的集成
以 Django 为例,在 manage.py 和 wsgi.py 中加载:
# manage.py
import os
from dotenv import load_dotenv
def main():
load_dotenv() # 必须在 Django 启动前加载
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myproject.settings")
# ... 其余代码
Flask 项目更简单,直接在 app.py 开头加载即可。
4.4 核心知识体系
下面这张图,是我自己总结的环境变量安全管理流程:
4.5 避坑指南
最后,分享几个我踩过的坑:
- 我曾经把 .env 文件放在项目根目录,但忘了加 .gitignore。 结果 git status 时才发现差点把生产库密码推上去。现在我的习惯是:创建项目第一件事就是配 .gitignore。
- 不要在 .env 里写注释敏感信息。 比如
# 密码是 admin123,这种注释跟明文没区别。 - 生产环境不要用 .env 文件。 生产环境建议用 Kubernetes Secrets、AWS Secrets Manager 或 HashiCorp Vault。.env 只适合本地开发。
- 定期轮换密钥。 我每三个月会强制团队更新一次数据库密码和 API Key,即使没人泄露也要换。
好了,这一章的内容就这些。环境变量安全不是什么高深技术,但它是 DevOps 的基本功。把这几条规则刻在脑子里,你的项目会少很多麻烦。