---
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")

这样做至少带来三个好处:

  1. 敏感信息完全不在代码库——代码里只有变量名,没有值;
  2. 开发、测试、生产可以各自拥有独立的配置,互不干扰;
  3. 完全符合《The Twelve-Factor App》里强调的「配置与代码分离」原则,你的应用会更具云原生气质。

2. 工具准备:安装 python-dotenv

python-dotenv 是一个极轻量的小工具,没有额外依赖,安装很简单:

pip install python-dotenv

如果你使用 requirements.txt 管理依赖,记得加上这一行:

python-dotenv>=1.0.0

安装完这个库,你就可以在项目里优雅地加载 .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. 安全避坑指南(必读)

  1. 坚决不提交 .env 到 Git——哪怕是私有仓库。私有仓库也可能会被泄露,或者某天突然转为公开而忘记清理历史记录。

  2. 生产环境绝不要留 fallback 密钥。像上面 BaseConfig 里的那些默认值只适合测试,正式上线前一定要换成真实的长随机密钥。

  3. 用代码生成随机密钥,不要自己手敲:

    import secrets
    print(secrets.token_hex(16))  # 生成一个 32 位的十六进制密钥
  4. 禁止在日志中输出敏感信息。开发时或许可以临时打印一两次调试,但生产环境务必将所有密钥、密码从日志中彻底屏蔽。


7. 小结

今天我们系统地走了一遍 Python 项目通用的安全配置方案,核心逻辑可以总结为四步:

1. 安装工具:pip install python-dotenv
2. 本地配置:.env(秘密) + .env.example(模板) + .gitignore(黑名单)
3. 代码集成:load_dotenv() + os.getenv() + 多环境配置类
4. 生产部署:用系统/Docker/云平台环境变量代替本地 .env

💡 黄金法则代码里只放非敏感的默认配置(如端口号、缓存时间),所有真实密钥、密码、API Key、数据库密码等,一律通过环境变量注入,与代码彻底分离。


🔗 扩展阅读