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 高
⚠️ 我曾经见过一个团队,把 AWS 密钥写在了 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 核心知识体系

下面这张图,是我自己总结的环境变量安全管理流程:

环境变量安全管理流程 环境变量安全管理流程 敏感信息来源 存储方式:.env 文件(不提交 Git) 加载工具:python-dotenv 使用规范:os.getenv() + 无默认值 + 异常处理 最终目标:零硬编码 + 零泄露 💡 核心原则:敏感信息永远不进入代码仓库,永远不打印到日志,永远不给默认值

4.5 避坑指南

最后,分享几个我踩过的坑:

  • 我曾经把 .env 文件放在项目根目录,但忘了加 .gitignore。 结果 git status 时才发现差点把生产库密码推上去。现在我的习惯是:创建项目第一件事就是配 .gitignore。
  • 不要在 .env 里写注释敏感信息。 比如 # 密码是 admin123,这种注释跟明文没区别。
  • 生产环境不要用 .env 文件。 生产环境建议用 Kubernetes Secrets、AWS Secrets Manager 或 HashiCorp Vault。.env 只适合本地开发。
  • 定期轮换密钥。 我每三个月会强制团队更新一次数据库密码和 API Key,即使没人泄露也要换。
🚨 最后一条红线: 如果你发现某个 .env 文件已经被提交到了 Git 历史中,立即 删除并重置所有相关密钥。Git 历史里的记录删不干净,攻击者可以翻 commit 历史找到它。

好了,这一章的内容就这些。环境变量安全不是什么高深技术,但它是 DevOps 的基本功。把这几条规则刻在脑子里,你的项目会少很多麻烦。