.env 非常方便,几乎所有现代框架都会用它。但只要存放位置弄错一处,里面的所有机密就可能被任何人读取。本文从零开始讲解它的含义和防护方法。
再讲细一点
应用里有「随环境而变的值」和「不想给人看到的值」。比如数据库密码、外部服务的 API key、用来加密会话的密钥等。如果把这些直接写死在源代码里,一旦共享代码,秘密也会跟着一起泄露。
于是,把这些集中到 .env 这一个文件里,代码则以「环境变量」的形式读取它们。Next.js、Laravel、Rails 等许多框架都采用这种方式。
内容是纯文本,一行一个「名称=值」(以下的值都是占位符)。
APP_ENV=production
DB_HOST=127.0.0.1
DB_PASSWORD=your-db-password-here
PAYMENT_API_KEY=your-payment-api-key-here把配置放在代码之外的做法,因 Web 应用设计指南「The Twelve-Factor App」而普及。它判断配置是否被正确分离的标准是:代码库能否随时开源,而不泄露任何认证信息。.env 是在开发阶段实现这一思路的便利容器。
与相似概念的区别
下面是最常与 .env 混淆的几样东西。其中只有一部分适合存放机密。
| 名称 | 是什么 | 能放机密吗? |
|---|---|---|
| 环境变量 | 操作系统或容器传给运行中进程的值 | 可以(生产环境的常规做法) |
.env | 把环境变量汇总到一个文件中 | 可以,但绝不能放进公开目录或 Git |
.env.example | 只列出名称、值留空的样例 | 不可以(本来就是要提交的) |
NEXT_PUBLIC_* / VITE_* | 被嵌入浏览器端 JavaScript 的变量 | 不可以(任何人都能读取) |
| 密钥管理服务 | 存放机密值的云服务 | 可以;通过权限和审计日志管理 |
第四行最容易被忽视。Next.js 会把以 NEXT_PUBLIC_ 开头的变量、Vite 会把以 VITE_ 开头的变量的值,在构建时嵌入发送给浏览器的 JavaScript。即使写在 .env 里,带这些前缀的变量也会变成公开的。
为什么是「最危险的文件」
.env 在一个文件里汇集了多把钥匙,所以这里一旦泄露,损害就会一下子扩散开来。把每把钥匙「泄露后会发生什么」并列出来,就能看出有多严重。
| 泄露的值 | 会发生什么 |
|---|---|
| 数据库认证信息 | 数据被读取、篡改、全部删除 |
| 外部 API key | 被冒名使用、违规扣费 |
加密密钥(如 Laravel APP_KEY、签名密钥) | 会话伪造、冒名登录 |
| OAuth 机密 | 关联应用被接管 |
事实上,.env 被公开是事故的经典戏码。→ .env 曾向全世界公开的故事
真实案例:被公开的 .env 成为攻击目标
- 大规模勒索活动(2024 年):据一家安全公司的研究团队报告,有一项活动以 11 万个域名为目标,收集暴露在 Web 服务器上的
.env文件,利用其中的云认证信息窃取数据并索要赎金。被收集的变量超过 9 万个,其中约 7,000 个属于云服务,约 1,500 个属于社交媒体账号。原因并非云服务商的漏洞,而是公开了.env的组织自身的配置错误 - Vercel(2026 年 4 月):内部系统遭未授权访问,导致部分客户的环境变量被读取——被读取的是存放在「值可以再次显示」类别中的变量 → 详细文章
- 公开仓库(2026 年的一项研究):发布在 GitHub 上的认证信息中,有超过 54 万个在研究时仍然有效。只要认证信息没有被吊销,删除文件或历史都无济于事 → 详细文章
第一个案例说明,被公开的 .env 不是被人偶然浏览发现的,而是被自动化地大规模收集。不要以为小网站就不会被发现。
泄露的典型模式=存放位置的失误
最大的原因是「把应用本体整个放进公开目录(docroot)」。本该只暴露 public/,结果却公开了它的上级目录,连 .env 都能被取到。
✗ 危险(本体直接放在公开根目录下)
public_html/ ├─ .env ← 能被读到! ├─ app/ └─ public/
✓ 安全(本体在外,只公开 public)
app-root/ ├─ .env ← 无法到达 ├─ app/ └─ public_html/ → public/
除了存放位置,它还会通过以下途径泄露:
- 提交到 Git(公开仓库会立刻公开;私有仓库中所有有访问权限的人都能看到)
- 把
.env.bak、.env.old等备份留在公开目录中 - 发布容器镜像或分发用 zip 时,
.env仍包含在内 - 如上所述,把机密放进
NEXT_PUBLIC_/VITE_变量
防护方法
不要放进公开目录
应用本体放在 docroot 之外,公开的只有 public/ 等。在虚拟主机上的具体步骤见 这篇文章。
不要提交到 git
在 .gitignore 里加入 .env*,只共享把值留空的 .env.example。
泄露后全部轮换
不只是能确认到的那一个,而要把 .env 里的钥匙全部更换。按照已被看到的前提来行动。
定期自查
养成确认自己网站上 /.env 是否能被打开的习惯。
如何检查自己的网站和仓库
自查(仅限你自己拥有的域名)
# 返回 200 且有正文就说明已公开。403/404 则暂时没问题
curl -sI https://你的域名/.env | head -1
curl -sI https://你的域名/.git/config | head -1养成每次发布都做一次这项确认的习惯,就能及早发现存放位置的失误。
网站外部和仓库内部都要检查。
- 对于已验证所有权的网站,网站安全诊断会一次性检查
/.env、/.env.local、/.env.bak等路径 - 在仓库中运行
git log --all --oneline -- .env,查看它是否曾被提交过。如果有输出,请吊销其中包含的钥匙 - 把配置或代码贴进机密泄露扫描器,找出写死的 API key 或私钥(在浏览器中运行)
- 检查
NEXT_PUBLIC_/VITE_变量的列表,确保没有机密被打包进构建后的 JavaScript
本站观点:.env 的存放位置和钥匙的吊销方法要一起决定
大多数 .env 事故,都始于一个放错位置的文件。而让损害在之后扩大的,是没有事先定好更换钥匙的流程。
本站建议:如果使用 .env,不仅要把它放在公开目录之外、不进入 Git,还要事先写下其中每把钥匙的吊销和重新签发方法。在生产环境中,改用云端的密钥管理服务,或改用「值无法再次显示」的环境变量,可以进一步减少泄露途径。
出处(一手资料)
- The Twelve-Factor App, "III. Config" — 12factor.net(把配置存放在环境变量中;能否开源的判断标准)
- Next.js docs, "Environment Variables" — nextjs.org(
NEXT_PUBLIC_会在构建时被内联进 JavaScript;不要提交 .env 文件) - Vite docs, "Env Variables and Modes" — vite.dev(
VITE_变量会暴露给客户端代码) - Unit 42 (Palo Alto Networks), "Leaked Environment Variables Allow Large-Scale Extortion Operation in Cloud Environments" (August 15, 2024) — unit42.paloaltonetworks.com