直接写 .env 文件不等于安全,因其易被误放至 Web 可访问路径、Git 误提交、加载时机不当或配置注入方式错误,导致密钥泄露。

为什么直接写 .env 文件不等于安全?
把数据库密码、API密钥写进 .env 文件,再用 python-dotenv 加载,并不自动防止泄露。关键在于:Flask 默认不会拒绝访问静态文件目录外的路径,而如果 .env 被误放到 Web 可访问目录(比如和 static/ 同级),攻击者可能直接 GET 到它——尤其是当服务器配置宽松或开发时用了 flask run --reload 且未设 extra_files 排除时。
实操建议:
-
.env必须放在项目根目录(即app.py或__init__.py所在层),且**绝不能**出现在static/、templates/或任何被 Flask 静态路由覆盖的路径下 - 启动前加一行检查:
assert not os.path.exists('static/.env') and not os.path.exists('templates/.env'),CI 或本地 pre-commit 阶段跑一下 - 用
dotenv.load_dotenv()时显式指定路径:dotenv.load_dotenv(dotenv_path='.env', override=False),避免意外加载到同名但位置错误的文件
load_dotenv() 的调用时机和作用域陷阱
很多人在 app.py 开头就调用 load_dotenv(),但若 Flask app 是通过工厂函数创建(create_app()),且该函数被多次导入(如测试、CLI 命令、gunicorn 多 worker),load_dotenv() 可能重复执行——虽然不会报错,但 override=False(默认)时后加载的值不会覆盖先加载的,容易造成配置不一致。
实操建议:
- 只在应用入口点(如
if __name__ == '__main__':块内,或 WSGIapplication.py顶层)调用一次load_dotenv() - 工厂函数内部**不要**调用
load_dotenv();改用os.getenv('DB_URL')直接读取,依赖外部已加载的环境 - 测试时用
pytest的monkeypatch.setenv()替代重载.env,避免污染全局os.environ
Git 里漏掉 .gitignore 会导致配置硬编码进仓库
python-dotenv 再规范也救不了提交了 .env 的仓库。一旦推送到 GitHub,密钥就永久留在 commit 历史里——就算删了文件、加了 .gitignore,历史记录仍可被 clone 下来。
实操建议:
- 初始化项目时第一件事:往
.gitignore加一行.env,并确认没被!*.env之类规则意外取消忽略 - 用
git check-ignore -v .env验证是否真被忽略 - 如果已经误提交,立刻用
git filter-repo --invert-paths --path .env彻底清除(需强制推送,团队同步更新) - 生产环境禁止使用
.env:Docker 用--env-file,K8s 用Secret挂载,python-dotenv仅用于本地和 CI 测试
Flask config.from_object() 和 os.getenv() 混用的风险
有人喜欢在 Config 类里写 SQLALCHEMY_DATABASE_URI = os.getenv('DB_URL'),看似干净,但 Flask 的 config.from_object() 是在类定义时求值的——如果此时 load_dotenv() 还没执行,os.getenv() 返回 None,后续再调用 load_dotenv() 也晚了,配置已固化。
实操建议:
- 所有敏感配置项(
SECRET_KEY、SQLALCHEMY_DATABASE_URI等)必须在app.config.update()或app.config.from_mapping()中动态赋值,且发生在load_dotenv()之后 - 或者改用延迟计算:定义一个函数
get_db_uri(),在需要时才调用os.getenv(),但注意 SQLAlchemy 初始化等地方可能不支持 callable - 最稳方案:用
app.config['SQLALCHEMY_DATABASE_URI'] = os.getenv('DB_URL')显式覆盖,别依赖类属性
真正危险的不是 python-dotenv 本身,而是人误以为“用了它就安全了”。文件位置、Git 状态、加载时机、配置注入方式——四个点里只要漏掉一个,密钥就可能裸奔。尤其要注意 CI 日志是否打印了 os.environ 内容,那是另一个常见泄露口。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











