Skip to main content

阅读: Do not use secrets in environment variables and here's how to do it better

·1418 words·3 mins
Table of Contents

Do not use secrets in environment variables and here’s how to do it better 的阅读笔记

为什么不应该在环境变量中存储敏感信息
#

Reason 1: 环境变量中的敏感信息通常管理不善
#

  1. 泄露后难以更改: 存储在环境变量中的敏感信息一旦被泄露,通常需要重启或重新部署整个应用才能进行更换,并且我们需要知道哪些服务正在使用被泄露的密钥

  2. 未定期轮换: 密钥通常需要定期轮换,因为可能早已在你未察觉的情况下泄露,导致安全隐患。而环境变量通常在基础设施部署时被设置好,密钥长期保持不变

  3. 难以审计: 环境变量缺乏内置的审计功能。无法追踪谁访问了某个密钥、它上次被修改的时间,或者它在系统中的具体使用情况

  4. 未加密存储: 环境变量通常以明文存储,可以通过 /proc/PID/environ 读取出来

Reason 2: SSR 架构下泄露敏感信息
#

同一代码库可以同时运行在客户端和服务器端,配置错误可能导致密钥暴露给前端。

Reason 3: .env 文件易泄露
#

  1. 违反关注点分离原则(Separation of Concerns): 配置密钥 是两种不同的概念,不应该混在一起管理。将敏感信息存入 .env 文件,会导致二者的界限模糊,使得管理和维护变得更加复杂

  2. 引入安全风险: .env 文件很容易被意外提交到 Git 仓库

Reason 4: 日志泄露敏感信息
#

  • 服务器的错误日志

  • 详细的调试输出

  • 异常崩溃报告

应用程序的日志可能无意中记录环境变量中的敏感信息。被记录到日志后,可能存储在本地磁盘、日志管理系统或日志服务中,而这些存储位置一旦发生数据泄露或未授权访问,敏感信息也会随之暴露。比如 CVE-2019-5483

Reason 5: 子进程默认继承环境变量
#

子进程默认继承环境变量,任何存储在环境变量中的密钥都会自动被所有子进程访问,即使它们实际上并不需要这些信息。这种行为违反了安全中的最小权限原则(Principle of Least Privilege)

Reason 6: 环境变量可在进程列表中被查看
#

在大多数类 Unix 操作系统(包括 Linux 和 macOS)上,系统上的任何用户都可以查看正在运行的进程的环境变量。这意味着存储在环境变量中的机密可能对系统上的所有用户都可见,而不仅仅是进程的所有者。比如执行 ps eww 或者 ps auxwe

Reason 7: 构建参数(Build Arguments)和 .env 文件泄露到 Docker 镜像
#

更好的密钥管理方案
#

三条原则:

  1. 始终将应用程序的配置与密钥分离
  2. 密钥应该由外部资源注入到应用程序中,并且应采用临时且短生命周期的访问方式,或者由应用程序在运行时动态请求。这可以分为以下几种方式:
    • 基础设施编排机制(如 Kubernetes sidecar 容器)执行安全的数据获取,并通过临时文件提供给应用程序,在应用程序启动时进行读取,或者将密钥以秘密卷挂载的方式提供给应用容器(这种方法在 Docker Swarm 和 Kubernetes 中较为流行)。另一种选择是通过环境变量提供一次性、短生命周期的密钥(通常称为 Token)。
    • 使用外部的密钥管理服务,应用程序首先通过临时的、短生命周期的 Token 进行身份验证,然后在运行时主动从该服务获取密钥。
    • 使用云厂商的密钥管理服务,允许应用程序在代码中直接获取密钥,并依赖云基础设施的身份认证与授权(IAM) 来确保安全性。
  3. 密钥应该通过密钥管理系统(Key Management Service, KMS)进行管理

Secret Zero 问题: 我们需要一个密钥来访问密钥管理服务,而这个初始密钥本身也是一种密钥。

为了解决这个问题,区分存储密钥和提供密钥很重要。如果应用程序需要一个单一的密钥来启动并访问密钥管理服务,那么你可以通过环境变量提供初始密钥,只要这个初始密钥(在许多产品中通常称为 Token,如 Hashicorp 的 Vault)是短生命周期且使用次数有限的。一旦应用程序已经启动并和密钥管理服务验证成功,应用程序后续将使用临时令牌进行通信以获取所需要的密钥,而初始密钥则会被过期消毁