---
title: 环境变量与安全配置
description: 使用 python-dotenv 加载环境变量,从 .env 文件配置敏感信息,隐藏密钥不上传代码仓库。
---
# 环境变量与安全配置:从 .env 加载配置,彻底隐藏密钥
> 📂 所属阶段:第六阶段 — 上线部署(生产篇)
> 🔗 相关章节:[environment-setup]() · [密码安全加密]()
---
## 0. 一个真实的“差点翻车”场景
上个月在帮新人复盘项目时,我一眼扫到阿里云 OSS 的 AccessKey Secret 直接“裸奔”在 django 的 `settings.py` 文件里。万幸的是,他还没把代码推送到公共仓库——但光是想象一下后果就让人后背发凉:代码一旦公开,免费额度可能在几个小时内被恶意爬虫占满,账单直接飙到几千块。
这就是**硬编码敏感信息**的致命之处:只要你的密钥出现在 Git 历史、代码片段分享或者任何第三方托管平台的备份里,它基本就不再秘密了。
那么,怎么彻底避免这种事?今天要聊的主角——**用 python-dotenv 管理的环境变量**,就是几乎所有 Python 项目都在用的低成本、高安全配置方案。
---
## 1. 核心逻辑:为什么要选择环境变量?
我们先看两种配置方式,差别一目了然。
### ❌ 错误示范:直接写在代码里
```python
# app/config.py(千万别这么干!)
SECRET_KEY = "dev-12345678abcdefgh"
AWS_ACCESS_KEY_ID = "AKIA2V3X7Y8ZABCDEF"
AWS_SECRET_ACCESS_KEY = "rT6uI9oP0qW2eR4tY5uI6oP7aS8dF9gH0jK1l"
就算你本地加了 .gitignore,万一哪天不小心删掉了、给同事打包代码时忘了过滤、或者别人用 git reflog 看历史,这密钥直接就暴露了,风险 100%。
✅ 正确方案:用环境变量隔离
# app/config.py(安全!)
import os
SECRET_KEY = os.getenv("SECRET_KEY")
AWS_ACCESS_KEY_ID = os.getenv("AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = os.getenv("AWS_SECRET_ACCESS_KEY")
这样做至少带来三个好处:
- 敏感信息完全不在代码库——代码里只有变量名,没有值;
- 开发、测试、生产可以各自拥有独立的配置,互不干扰;
- 完全符合《The Twelve-Factor App》里强调的「配置与代码分离」原则,你的应用会更具云原生气质。
2. 工具准备:安装 python-dotenv
python-dotenv 是一个极轻量的小工具,没有额外依赖,安装很简单:
pip install python-dotenv
如果你使用 requirements.txt 管理依赖,记得加上这一行:
安装完这个库,你就可以在项目里优雅地加载 .env 文件了。
3. 本地开发:三大配置文件各司其职
为了让本地开发、团队协作和生产部署都能顺畅运转,我们通常需要准备三类配置文件。
3.1 .env —— 本地秘密仓库(绝对不能上传)
这个文件里放的都是真实的敏感配置,比如开发环境的密钥、本地数据库地址等。永远不要把它提交到 Git!
# .env(必须加入 .gitignore!)
FLASK_APP=app/__init__.py
FLASK_ENV=development
SECRET_KEY=dev-only-very-long-random-secret-key-change-in-production
DATABASE_URL=sqlite:///daoman_dev.db
JWT_SECRET=dev-jwt-super-long-secret-key-change-now
MAIL_SERVER=smtp.qq.com
MAIL_PORT=587
MAIL_USERNAME=your_qq_email@qq.com
MAIL_PASSWORD=your_qq_authorization_code
3.2 .env.example —— 团队共享模板(一定要上传)
这个文件是给队友和未来的自己看的“使用说明书”——里面只保留配置项的格式和占位说明,绝不包含任何真实密钥。
# .env.example(放心上传 Git,队友复制一份改名 .env 就能用)
FLASK_APP=app/__init__.py
FLASK_ENV=development
SECRET_KEY=change-this-to-a-32-character-random-secret
DATABASE_URL=sqlite:///daoman_dev.db
JWT_SECRET=change-this-to-a-32-character-random-jwt-secret
MAIL_SERVER=smtp.example.com
MAIL_PORT=587
MAIL_USERNAME=your@email.com
MAIL_PASSWORD=your-email-password-or-authorization-code
3.3 .gitignore —— Git 黑名单(必须配置)
我们要教会 Git 自动忽略 .env 这一类敏感文件,顺便加上 Python 项目的常规忽略项:
# .gitignore
.env
.env.local
.env.production
*.db
*.sqlite3
__pycache__/
*.pyc
*.pyo
*.pyd
venv/
.venv/
.coverage
htmlcov/
static/uploads/
*.log
这样一来,任何以 .env 开头的文件都会被 Git 自动忽略,大大降低了误提交的风险。
4. 代码集成:加载并使用环境变量
接下来我们把这些配置真正接入到项目中。无论是 Flask、django、FastAPI 还是普通的 Python 脚本,思路都几乎一样。
下面以 Flask 为例,我们使用一个 config.py 模块来统一管理。
多环境配置设计(config.py)
我们可以把配置拆成“基础配置”、“开发配置”、“测试配置”、“生产配置”四个类,结构清晰,切换环境也很方便。
# app/config.py
import os
from dotenv import load_dotenv
# 本地开发和测试环境下,我们会用到 .env 文件
load_dotenv()
class BaseConfig:
"""所有环境通用的基础配置"""
SECRET_KEY = os.getenv("SECRET_KEY", "unsafe-fallback-secret-only-for-testing")
SQLALCHEMY_DATABASE_URI = os.getenv("DATABASE_URL", "sqlite:///app_default.db")
SQLALCHEMY_TRACK_MODIFICATIONS = False # 关闭追踪,节省内存
# Session 安全设置(生产环境务必开启)
SESSION_COOKIE_SECURE = os.getenv("FLASK_ENV") == "production"
SESSION_COOKIE_HTTPONLY = True
SESSION_COOKIE_SAMESITE = "Lax"
# 邮件配置(如果你的项目不用邮件,可以删掉)
MAIL_SERVER = os.getenv("MAIL_SERVER")
MAIL_PORT = int(os.getenv("MAIL_PORT", 587))
MAIL_USE_TLS = os.getenv("MAIL_USE_TLS", "True").lower() == "true"
MAIL_USERNAME = os.getenv("MAIL_USERNAME")
MAIL_PASSWORD = os.getenv("MAIL_PASSWORD")
class DevelopmentConfig(BaseConfig):
"""本地开发"""
DEBUG = True
TESTING = False
class TestingConfig(BaseConfig):
"""跑测试的时候用"""
DEBUG = True
TESTING = True
# 测试用内存数据库,跑完自动清空
SQLALCHEMY_DATABASE_URI = "sqlite:///:memory:"
WTF_CSRF_ENABLED = False
class ProductionConfig(BaseConfig):
"""生产环境"""
DEBUG = False
TESTING = False
@classmethod
def init_app(cls, app):
super().init_app(app)
# 生产环境必须提供真正的 SECRET_KEY,否则拒绝启动
if app.config["SECRET_KEY"] == "unsafe-fallback-secret-only-for-testing":
raise ValueError("生产环境必须设置真实的 SECRET_KEY!请检查系统环境变量。")
if not app.config["MAIL_PASSWORD"] and app.config.get("MAIL_USE_TLS"):
raise ValueError("生产环境邮件服务需要配置真实的密码!")
# 环境映射
config = {
"development": DevelopmentConfig,
"testing": TestingConfig,
"production": ProductionConfig,
"default": DevelopmentConfig
}
在 Flask 工厂函数中引入(init.py)
# app/__init__.py
from flask import Flask
from app.config import config
def create_app(config_name="default"):
app = Flask(__name__)
app.config.from_object(config[config_name])
# 这里继续绑定数据库、注册蓝图……
# 例如:
# from app.models import db
# db.init_app(app)
return app
要点:
- 本地运行时,
load_dotenv() 会从 .env 文件读入环境变量。
- 生产部署时,我们完全不会依赖
.env,而是直接注入系统或平台的环境变量。
5. 生产环境:用系统/云平台变量替代本地 .env
在生产环境,我们不会也没必要再放一个 .env 文件。正确的做法是通过以下三种方式之一注入配置:
方式一:直接在服务器上设置环境变量
# Linux/macOS
export FLASK_ENV=production
export SECRET_KEY="prod-32-character-random-secret-key-abcd1234"
export DATABASE_URL="postgresql://user:password@db-server:5432/myapp_prod"
# Windows PowerShell
$env:FLASK_ENV="production"
$env:SECRET_KEY="prod-32-character-random-secret-key-abcd1234"
方式二:用 Docker 容器注入
Dockerfile 中可以设置默认值,但真正敏感的变量要在启动时传入:
# Dockerfile
FROM python:3.11-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
ENV FLASK_APP=app/__init__.py
ENV FLASK_ENV=production
CMD ["gunicorn", "app:create_app('production')", "--bind", "0.0.0.0:8000"]
启动容器时覆盖敏感值:
docker run -d \
-p 8000:8000 \
-e SECRET_KEY="prod-32-character-random-secret-key-abcd1234" \
-e DATABASE_URL="postgresql://user:password@db-server:5432/myapp_prod" \
my-flask-app:latest
方式三:接入云平台密钥管理服务(更推荐)
如果项目有合规或安全审计需求,建议直接用云厂商的密钥管理服务:
- 阿里云:KMS 密钥管理服务
- 腾讯云:云 API 密钥 + KMS
- AWS:Secrets Manager
- Heroku:Config Vars
- Vercel:Environment Variables
这些服务不仅将密钥加密存储,还能做到密钥自动轮换、访问权限控制,并且绝不会被打印到日志中,安全性又上了一个台阶。
6. 安全避坑指南(必读)
-
坚决不提交 .env 到 Git——哪怕是私有仓库。私有仓库也可能会被泄露,或者某天突然转为公开而忘记清理历史记录。
-
生产环境绝不要留 fallback 密钥。像上面 BaseConfig 里的那些默认值只适合测试,正式上线前一定要换成真实的长随机密钥。
-
用代码生成随机密钥,不要自己手敲:
import secrets
print(secrets.token_hex(16)) # 生成一个 32 位的十六进制密钥
-
禁止在日志中输出敏感信息。开发时或许可以临时打印一两次调试,但生产环境务必将所有密钥、密码从日志中彻底屏蔽。
7. 小结
今天我们系统地走了一遍 Python 项目通用的安全配置方案,核心逻辑可以总结为四步:
1. 安装工具:pip install python-dotenv
2. 本地配置:.env(秘密) + .env.example(模板) + .gitignore(黑名单)
3. 代码集成:load_dotenv() + os.getenv() + 多环境配置类
4. 生产部署:用系统/Docker/云平台环境变量代替本地 .env
💡 黄金法则:代码里只放非敏感的默认配置(如端口号、缓存时间),所有真实密钥、密码、API Key、数据库密码等,一律通过环境变量注入,与代码彻底分离。
🔗 扩展阅读