不喜欢广告? 无广告 今天

.env 文件——6个导致你的密钥出现在 GitHub 上的错误

更新于

大多数 .env 文件泄露并非来自黑客——而是来自开发者在设置 .gitignore 之前就提交了文件,或在发布时将包含真实值的 .env.example 文件送出,或让框架默默地将服务器密钥打包到客户端 JS 中。以下是实际上发生的6个错误。

.env 文件——6 个导致密钥暴露到 GitHub 的错误 1
广告 移除?

GitGuardian 2023 年“机密泄露”报告发现,有超过 1200 万个机密被提交到了公共 GitHub 仓库中。其中大多数并非被盗,而是由开发者误以为自己已经妥善处理而上传的。这些就是导致此类问题的模式。

1. 在首次提交后添加 .gitignore 文件

.gitignore 仅阻止 未被跟踪 文件将被纳入暂存状态。一旦文件被跟踪——哪怕只是短暂地——它就会进入 git 历史记录。如果你已经创建了 .env,运行了 git add . && git commit,然后在之后添加了 .env.gitignore ,那么该文件仍会出现在所有在该变更之前的提交中。

检查它是否已存在于历史记录中:

git log --all -- .env

如果返回了提交记录,说明机密已存在于历史记录中。首先轮换凭证。然后使用 git-filter-repogit filter-branch):

pip install git-filter-repo
git filter-repo --path .env --invert-paths

的推荐替代方案)

向所有远程仓库强制推送,并通知团队成员重新克隆。在清除操作之前,所有已克隆的副本中都包含这些提交——包括任何从仓库检出的自动化 CI 系统。

2. 将 .env 复制到 .env.example 但未清除值 .env 标准工作流程:创建一个包含真实值的 .env.example ,然后将其复制到

cp .env .env.example ,以向团队成员展示项目所需的密钥。问题就出在复制步骤上。 会复制所有内容——包括密钥 .env.example 和值。而 .env.example 本意就是被提交到仓库中的。真实值在

是故意放入仓库的。

DATABASE_URL=postgres://admin:supersecretpassword@prod-db.example.com/appdb
STRIPE_SECRET_KEY=sk_live_51AbcDefGhiJklMnopQrstUvwx...
JWT_SECRET=my-actual-production-jwt-secret

❌ 最终进入 git 的内容: .env.example ✅ 正确的内容应为:

DATABASE_URL=postgres://user:password@localhost:5432/appdb
STRIPE_SECRET_KEY=sk_live_YOUR_KEY_HERE
JWT_SECRET=generate-a-random-secret-min-32-chars

创造 .env.example 先使用占位符值创建文件,提交它,然后将其复制到 .env 并填写真实凭证——而不是反过来。

3. 在错误处理器中记录 process.env

这种情况最初只是在事件期间进行“快速调试”,之后从未被移除。或者它存在于看似无害的通用错误中间件中。

// Classic debug line that makes it to production
console.log('Starting with config:', process.env);

// Generic error handler that dumps everything
app.use((err, req, res, next) => {
  logger.error({ config: process.env, error: err.message });
  res.status(500).json({ error: 'Internal server error' });
});

process.env 运行时包含所有由 dotenv 加载的变量以及系统变量。将完整的对象传递给日志记录器意味着它会进入你的日志聚合器、错误追踪服务(如 Sentry、Datadog、Rollbar)以及可能的错误通知邮件或 Webhook 中。许多这些服务会将其转发到第三方存储,且具有各自访问控制权限。

仅记录诊断所需的特定值:

logger.error({
  nodeEnv: process.env.NODE_ENV,
  appVersion: process.env.APP_VERSION,
  error: err.message,
  stack: err.stack
});

4. 将密钥烘焙进 Docker 镜像层

两种模式会永久将密钥嵌入 Docker 镜像历史中:

# Pattern 1: COPY bakes the entire .env into a layer
COPY .env .

# Pattern 2: ARG/ENV burns values into build metadata
ARG DATABASE_URL
ENV DATABASE_URL=$DATABASE_URL

即使你在后续层中删除了该文件(RUN rm .env),其值仍可从镜像历史中读取。任何拥有镜像拉取权限的人都可以运行:

docker history --no-trunc your-image:tag

并恢复构建时使用的 ARG 值。Docker BuildKit 的密钥是正确的工具——它在构建过程中挂载密钥,而不会将其写入任何镜像层:

# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=db_url     DATABASE_URL=$(cat /run/secrets/db_url) ./setup.sh

对于运行时配置,通过 docker run -eenvironment: 在 Docker Compose 中注入环境变量——引用主机环境变量,而不是硬编码值,也绝不要使用 COPY‘d 密钥文件。

5. 使用弱占位符密钥并发布到生产环境

JWT_SECRET=secret, SESSION_SECRET=keyboard cat, APP_KEY=changeme, ENCRYPTION_KEY=1234567890abcdef。这些密钥最初是作为开发环境的占位符,有时甚至从未被替换。攻击者在暴力破解 JWT 签名时会主动尝试这些字符串——它们出现在特定的词典中,因为它们在 GitHub 搜索中频繁出现。

使用 HS256 和弱密钥签名的 JWT 可以通过像 c-jwt-cracker这样的工具在离线环境中被破解。仅拦截到一个有效的令牌就足以暴力破解密钥并生成任意令牌。

正确的密钥应具有加密随机性,长度至少为 32 字节。在需要时提前生成它们—— 环境密钥生成器 在 IO Tools 上将生成适用于常见 .env 密钥(如 JWT 密钥、会话密钥、API 密钥)的正确随机值,无需任何配置。从一开始就设置它们;不要使用占位符并计划在“上线前修复”。

6. 框架环境变量命名约定将密钥暴露给客户端

多个流行框架使用变量命名前缀来判断客户端与服务器的可见性。如果处理不当,就会将密钥以明文形式打包到 JavaScript 文件中,发送给加载应用的每个浏览器。

  • Next.js: 仅在根目录 NEXT_PUBLIC_以 -prefixed 变量会被打包到客户端。但当通过 getServerSideProps props 传递时,服务器端密钥会泄露——任何在 props 中返回的值都会被序列化到页面 HTML 中,且可在源码中被读取。
  • Vite:VITE_ 前缀的变量会被打包到客户端 JS 中。使用 VITE_DATABASE_URL “方便起见”是开发者实际犯下的错误。
  • Create React App: 全部 REACT_APP_ 变量最终会出现在客户端包中,没有例外。不存在服务器端的 CRA 运行时——所有加载内容都会发送到浏览器。

构建完成后,搜索输出目录以查找已知密钥值:

grep -r "sk_live_" ./dist
grep -r "sk_live_" ./.next/static

如果返回任何匹配项,说明这些密钥存在于每个浏览器标签页中。立即轮换它们,并检查还有哪些内容被打包。

一个值得从一开始就养成的习惯

在创建任何将存储密钥的文件之前,先设置 .gitignore ——而不是事后补救。任何新仓库的首次提交应为 .gitignore.env.example ,并使用占位符值。将 .gitignore 生成器 将在一分钟内生成一个完整的、针对特定框架的忽略文件。

上述六种错误在任何代码编写之前都是可以避免的。一旦密钥暴露,唯一的修复方式是全面轮换——包括服务提供商、所有曾拥有副本的环境,以及可能在日志中缓存该值的系统。

想要享受无广告的体验吗? 立即无广告

安装我们的扩展

将 IO 工具添加到您最喜欢的浏览器,以便即时访问和更快地搜索

添加 Chrome 扩展程序 添加 边缘延伸 添加 Firefox 扩展 添加 Opera 扩展

记分板已到达!

记分板 是一种有趣的跟踪您游戏的方式,所有数据都存储在您的浏览器中。更多功能即将推出!

广告 移除?
广告 移除?
广告 移除?

新闻角 包含技术亮点

参与其中

帮助我们继续提供有价值的免费工具

给我买杯咖啡
广告 移除?