2026年,你还在用python-dotenv吗?

infisical 发布于 2026-07-28 阅读 32

本文探讨了2026年是否应该继续使用python-dotenv库来管理Python项目的环境变量。文章指出,对于单人项目或原型开发,python-dotenv和.env文件仍然是简单有效的方案。但当项目涉及多个开发者、多个环境或生产部署时,.env文件会带来安全风险(如泄露)、共享困难、缺乏版本控制、无法自动轮换密钥以及缺少审计追踪等问题。文章介绍了os.environ、pydantic-settings等替代方案,并重点推荐使用秘密管理平台(如Infisical)来集中管理密钥,通过CLI或SDK注入环境变量,从而实现安全、可审计的密钥管理。最终结论是,团队项目应尽早迁移到秘密管理工具。

Blog image

每个 Python 项目最终都需要使用数据库密码或 API 密钥这类秘密,应用在运行时会用到它们。但这些凭证绝不应出现在源代码中,否则任何有仓库访问权限的人(或拿到泄露副本的人)都能读取。

最快的方式是将值硬编码到需要它的文件中,而问题就在于此:文件被推送的那一刻,密钥就公开了,并且即使后续删除了那一行,它也会永远留在 Git 历史中,从而制造了安全漏洞。

大多数项目通过将值完全移出代码来解决这个问题。首先,将其放入环境变量——这是操作系统提供给运行程序的一段配置,在 Python 中通过 os.environ 读取——这是标准库提供的类似字典的视图,显示当前进程中设置的所有变量。

这种方法在每次手动设置几个变量变得繁琐之前都还管用,通常到了这个节点,python-dotenv 就会登场:一个小的库,读取 .env 文件(一个包含 KEY=value 行的纯文本文件)并自动将其加载到环境中,这样没人需要在每次会话前输入 export。这种演进对于个人项目来说没问题。但一旦有第二个开发者、第二个环境或生产部署加入,它就开始吃力了。

Python 生态系统和任何 dotenv 方案都有相同的习惯和相同的瓶颈。所以值得直接问一句:在 2026 年,你还应该使用 python-dotenv 吗?

Python 项目今天如何处理环境变量

大多数 Python 代码库通过几种模式处理秘密,通常随着项目增长按以下顺序出现:

os.environos.getenv() 是标准库的基础。没有依赖,没有配置,只有进程环境中已经设置的变量:

import os

database_url = os.environ["DATABASE_URL"]
debug = os.getenv("DEBUG", "false") == "true"

这在开发者需要在本地每次启动应用时手动在 shell 中设置几个变量之前都工作得很好。

python-dotenv 解决了这个摩擦。一个 .env 文件位于项目根目录,load_dotenv() 在应用其余部分启动前将其读入 os.environ

from dotenv import load_dotenv
import os

load_dotenv()
database_url = os.environ["DATABASE_URL"]

这就是 python-dotenv 几乎成为默认选择的原因。它是一个便利层,将文本文件转换为环境变量,而应用代码读取这些变量的方式与没有文件时完全相同。

然后是 pydantic-settings。它在类型化代码库和 FastAPI 应用中越来越常见。它不是通过名称读取松散的环境变量,而是通过一个 Settings 类将它们声明为类型化字段,并在启动时验证所有必需的字段实际存在:

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    database_url: str
    debug: bool = False

settings = Settings()

pydantic-settings 本质上是 python-dotenv 的一个包装,工作在相同的 .env 文件之上,只是在此基础上添加了验证和类型化。

python-decouple。一个更小的库,做与 python-dotenv 相同的工作,但接口不同,通过 config() 调用从 .env 文件或 settings.ini 读取配置,将配置与代码分离。

keyring 则完全是另一个类别。它将凭证存储在操作系统的原生凭证存储中——macOS 上的 Keychain,Windows 上的 Credential Locker,Linux 上的 Secret Service 或 GNOME Keyring。这使得它适合运行在开发者自己机器上的 CLI 工具。但不适合任何部署到服务器或容器中的场景,因为无头环境通常没有操作系统钥匙链可供交互。

python-dotenv 和 .env 文件在一个团队中为何会出问题

这些工具都没有改变 .env 文件的本质:一个包含你最敏感凭证的纯文本文件,放在项目文件夹中。对于单个开发者的业余项目来说这没问题,但一旦有团队、多个环境以及生产部署参与进来,就很难再辩护了,而且问题不仅仅是安全性。有些问题同样关乎日常运行团队的摩擦。

.env 文件容易泄露,在 Python、Django、Flask 或其他任何地方都是如此。

位于项目根目录的 .env 文件正是那种容易意外提交的文件,尤其是在新队友尚未养成 .gitignore 习惯时的第一次推送,或者当你使用 AI Agent 却没有检查其工作时。

这之所以重要,是因为泄露的 DATABASE_URL 或 API 密钥不是理论上的风险,而是直接可用的凭证。公开的 GitHub 提交会在几分钟内被自动机器人扫描,寻找这种模式,一旦数据库密码泄露,拥有它的人可以直接连接到数据库,就像应用所做的那样。

这在实践中正是 secret sprawl 的大致面貌:不是一次戏剧性的泄露,而是同一个凭证被复制粘贴到十几个地方,直到没人能确定它还在哪里存活。

共享 .env 文件意味着 Slack、电子邮件或共享驱动器,而且不可扩展。

一旦有新开发者需要同样的 DATABASE_URL.env 文件就必须传播。除了通过电子邮件、Slack 或其他通讯应用(这些应用不会过期访问权限,也无法控制谁能看到消息)以明文共享秘密的安全问题外,还有操作问题。新队友必须找到当前文件持有者,希望文件是最新的,并且每次值变化时都要重复这个过程。没有人确保每个人拥有正确的版本,所以漂移是常态,而不是边缘情况。

.env 不支持自动密钥轮换。

如果共享 .env 文件中的某个密钥被泄露,修复意味着要手动找到并更新每个队友机器、每个 CI 作业和每个服务器上的每一个文件副本。实际上,这就是为什么被泄露的凭证在事件曝光后往往还会有效数月的原因。依赖手动秘密管理会破坏构建,因为新值不会自动传播。如果你遗漏了某处将副本替换为新值,轮换可能会导致宕机。

.env 文件没有审计追踪。

.env 文件无法告诉你谁读取了 DATABASE_URL 或者何时读取的。如果出了问题——无论是怀疑泄露还是仅仅是值变化后服务失败——都没有日志可查。回答“谁碰过这个,什么时候碰的”变成了四处打听并希望有人记得,而不是去查记录。

环境隔离只是命名约定,而不是边界。

同一个仓库中的 .env.development.env.production 依赖开发者记住该加载哪一个。没有什么强制这种隔离,这可能是操作风险迅速变得严重的地方:一个本应该测试本地数据库的脚本意外地加载了 .env.production(错误的符号链接、拼写错误、复制粘贴错误),它不会大声报错,而是静默地针对真实生产数据库运行,出问题的第一个迹象往往是事件本身,而不是事先警告。

给 Django、Flask 和 FastAPI 的快速提示

.env 文件的团队规模问题在各框架中都是一样的,因为它们实际上关乎值的来源,而不是框架如何读取它。

Django 和 Flask 应用读取配置的方式与其他任何 Python 进程一样,都是通过 os.environ,通常在开发中由 python-dotenv 填充。Infisical 的 Django 集成其 Python 指南中的 Flask 示例 都是通过在应用启动前注入真实环境变量来工作的,因此两个框架都不需要任何代码更改来脱离 .env 文件。

FastAPI 应用更可能特别使用 pydantic-settings,但底层机制不变。Settings 类仍然从 os.environ 读取,所以它会从任何在应用启动前设置变量的来源(.env 文件或其他)获取变量。

实际上什么替代了共享的 .env 文件

密钥管理器 是一类直接应对泄露风险、共享摩擦以及缺乏轮换和审计追踪问题的工具。

  • 不再让每个开发者、服务器和 CI 作业都持有一个各自的 .env 文件副本,而是将秘密保存在一个集中管理的加密存储中。
  • 访问权限授予特定的身份(人、服务器、CI 管道),而不是给任何碰巧有文件副本的人。
  • 每次读取都会被记录,轮换被泄露的凭证意味着在一个地方更改它,而不是寻找每一个有文件副本的地方。

Infisical 就是这样一个平台,针对现有 Python 应用尝试它的最直接方式甚至不需要触碰应用代码。

脱离现有的 .env 文件也不意味着需要手动重新输入每个值。Infisical 可以直接导入 .env 文件,一次性将每个 KEY=value 行读入选定的环境(开发、预发布、生产)。

登录并连接后,Infisical CLI 可以启动任何 Python 进程,并已注入真实秘密作为环境变量:

infisical run -- python manage.py runserver
infisical run -- flask run
infisical run -- uvicorn main:app

os.environ["DATABASE_URL"] 仍然像与 .env 文件时一样工作。pydantic-settings 仍然验证相同的字段。

唯一改变的是值的来源:一个集中管理、受访问控制的存储,而不是一个静态文件;每个环境(开发、预发布、生产)指向自己的一组秘密,而不是自己的文件。

对于需要注入静态值之外的情况,Python SDK 可以通过编程方式获取秘密:

from infisical_sdk import InfisicalSDKClient

client = InfisicalSDKClient(host="https://app.infisical.com")
client.auth.universal_auth.login(client_id, client_secret)

secret = client.secrets.get_secret_by_name(
    secret_name="DATABASE_URL",
    project_id=project_id,
    environment_slug="prod",
    secret_path="/",
)

这打开了静态 .env 文件永远无法做到的大门,比如 动态秘密,它按需生成一个短期数据库凭证,而不是无限期地重复使用同一个密码。

什么时候 dotenv 仍然是正确的选择

这些都不意味着 python-dotenv 是错误的选择。对于单个开发者的原型、一次性脚本或个人项目(没有队友、没有生产部署),os.environ.env 文件仍然是最简单的可用选项,添加密钥管理器会带来超出问题所需的额外基础设施。

一旦有第二个开发者、第二个环境或真实部署出现,就必须脱离 .env 文件。这不需要重写应用读取配置的方式,只需将 infisical run 指向已有的启动命令即可。Infisical 的秘密管理覆盖了从本地开发到生产的整个流程,如果后来证书或特权访问也成为需要管理的部分,同样可以依赖这个平台。

  • 原文链接: infisical.com/blog/shoul...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~

相关文章

0 条评论