Back

.env 文件与不提交机密信息的艺术

.env 文件与不提交机密信息的艺术

您是否曾在项目中看到 .env 文件并好奇它的用途?或者您是否听说过意外将 API 密钥提交到 GitHub 的危险?本指南将解释您需要了解的关于 .env 文件的一切——它们是什么、为什么重要,以及如何正确使用它们来保护您的机密信息。

关键要点

  • .env 文件将环境特定的配置和机密信息与您的代码分开存储
  • 永远不要将 .env 文件提交到版本控制系统
  • 使用 .env.example 文件来记录所需变量,而不暴露实际值
  • 在应用程序启动时验证环境变量
  • 随着团队和应用程序的增长,考虑更高级的解决方案

什么是 .env 文件?

.env 文件是一个简单的文本文件,以 KEY=value 格式存储环境变量。这些文件有一个关键目的:将敏感的配置数据与您的代码分离。

# 示例 .env 文件
API_KEY=a1b2c3d4e5f6g7h8i9j0
DATABASE_URL=postgres://username:password@localhost:5432/mydb
DEBUG=false

环境变量是可以影响运行中应用程序行为的值。通过将它们存储在 .env 文件中而不是在应用程序中硬编码,您可以获得几个优势:

  1. 安全性:敏感数据不会出现在您的代码库中
  2. 灵活性:不同的环境可以使用不同的配置
  3. 简单性:易于更新而无需更改代码

为什么存在 .env 文件:简史

.env 文件方法在 2012 年左右作为 十二要素应用 方法论的一部分而流行,该方法论建议将配置存储在环境中。在这种标准化之前,开发人员经常犯危险的错误:

  • 直接在代码中硬编码数据库凭据
  • 将 API 密钥存储在已提交的配置文件中
  • 在不同环境中使用不同的配置机制

这些做法导致了严重的安全漏洞。2016 年,Uber 遭受了大规模数据泄露,暴露了 5700 万用户的数据,因为开发人员在 GitHub 仓库中发布了 AWS 凭据。和解费用达 1.48 亿美元。

.env 文件的工作原理

.env 文件在概念上很简单,但在实践中很强大。它们通常的工作方式如下:

  1. 您在项目根目录创建一个 .env 文件
  2. 您以 KEY=value 格式添加环境特定的变量
  3. 应用程序中的库在运行时加载这些变量
  4. 您的应用程序代码通过环境变量访问这些值

最重要的部分:您的 .env 文件永远不应该提交到版本控制系统

在 Node.js 项目中设置 .env 文件

让我们通过一个使用 Node.js 的实际示例来演示:

1. 创建 .env 文件

在项目根目录中,创建一个名为 .env 的文件:

# API 凭据
API_KEY=your_secret_api_key
API_SECRET=your_secret_api_secret

# 数据库配置
DB_HOST=localhost
DB_USER=root
DB_PASS=password
DB_NAME=myapp

# 应用程序设置
PORT=3000
NODE_ENV=development

2. 将 .env 添加到 .gitignore

创建或更新您的 .gitignore 文件以包含:

# 环境变量
.env
.env.local
.env.*.local

3. 安装 dotenv 包

npm install dotenv --save

4. 在应用程序中加载环境变量

在主应用程序文件的最顶部(在任何其他代码之前):

require('dotenv').config();

// 现在您可以使用 process.env 访问变量
const apiKey = process.env.API_KEY;
const port = process.env.PORT || 3000;

console.log(`在端口 ${port} 上启动服务器`);

安全最佳实践

遵循这些最佳实践将帮助您避免常见的安全陷阱:

1. 永远不要将 .env 文件提交到版本控制

这是最重要的规则。检查您的 .gitignore 文件以确保排除了 .env

2. 创建模板 .env.example 文件

提供一个包含所需变量但不包含实际值的模板:

# API 凭据
API_KEY=
API_SECRET=

# 数据库配置
DB_HOST=
DB_USER=
DB_PASS=
DB_NAME=

# 应用程序设置
PORT=3000
NODE_ENV=development

这个文件应该提交到您的仓库,以帮助其他开发人员知道他们需要设置哪些变量。

3. 验证所需的环境变量

在应用程序启动时检查所有必需的变量是否存在:

const requiredEnvVars = ['API_KEY', 'DB_HOST', 'DB_USER', 'DB_PASS'];
const missingEnvVars = requiredEnvVars.filter(
  envVar => !process.env[envVar]
);

if (missingEnvVars.length > 0) {
  throw new Error(`缺少必需的环境变量: ${missingEnvVars.join(', ')}`);
}

4. 为不同环境使用不同的 .env 文件

对于更复杂的设置,您可能需要多个环境文件:

  • .env.development - 开发环境设置
  • .env.test - 测试环境设置
  • .env.production - 生产环境设置

常见挑战和解决方案

挑战:在团队成员之间共享机密信息

在团队成员之间安全地共享机密信息是棘手的。避免通过电子邮件或聊天发送凭据。

解决方案:

  • 使用具有共享功能的密码管理器
  • 考虑使用机密管理服务,如 DopplerHashiCorp Vault
  • 对于小团队,安全的加密渠道可能是可以接受的

挑战:管理多个环境

随着应用程序的增长,您需要跨环境管理变量。

解决方案:

  • 使用环境特定的 .env 文件(.env.development.env.production
  • 实现加载层次结构,其中 .env.local 覆盖 .env
  • 对于较大的团队,考虑环境变量管理工具

挑战:CI/CD 流水线集成

您的 CI/CD 流水线需要访问机密信息,但您不能提交 .env 文件。

解决方案:

  • 使用您的 CI/CD 提供商的机密管理(GitHub Secrets、GitLab CI/CD Variables)
  • 与机密管理服务集成
  • 在部署期间从安全源生成 .env 文件

超越基本用法

使用 TypeScript

对于 TypeScript 项目,您可以为环境变量添加类型安全:

// src/env.d.ts
declare namespace NodeJS {
  interface ProcessEnv {
    NODE_ENV: 'development' | 'production' | 'test';
    PORT: string;
    API_KEY: string;
    // 在这里添加其他变量
  }
}

Docker 和容器化

使用 Docker 时,您有几种处理环境变量的选项:

  1. 使用 --env-file 标志:

    docker run --env-file .env myapp
  2. 在您的 docker-compose.yml 中定义变量:

    services:
      app:
        image: myapp
        env_file:
          - .env

.env 文件的替代方案

虽然 .env 文件很流行,但它们不是唯一的解决方案:

方法优点缺点
.env 文件简单,广泛支持手动共享,无版本控制
环境变量原生操作系统支持,无需文件更难管理变量集
配置服务器集中化,版本化,访问控制设置更复杂,潜在单点故障
机密管理器安全,审计,访问控制成本,额外依赖

常见问题

可以,注释以 # 开头。

.env 文件在运行时由您的应用程序加载,而系统环境变量在操作系统级别设置。.env 变量只影响加载它们的特定应用程序。

使用环境特定的文件,如 .env.development 和 .env.production,或使用机密管理服务。

对于大多数应用程序,影响可以忽略不计。文件通常在启动时只读取一次。

使用安全的密码管理器或专用的机密管理工具,而不是电子邮件或聊天。

结论

通过使用 .env 文件将机密信息保持在代码库之外,您正在朝着更安全的应用程序开发迈出重要一步。记住:最安全的机密是永远不离开您本地环境的机密。

Listen to your bugs 🧘, with OpenReplay

See how users use your app and resolve issues fast.
Loved by thousands of developers