第三讲:明文 Secrets 的危害——从 Git 历史到环境变量

讲完 Secrets 的定义和分类,今天咱们聊点「疼」的。

明文 Secrets 到底能造成多大的破坏?我见过太多团队,直到被拖库、被删库、被勒索,才回头去翻自己的 Git 提交历史。

你会发现,那些「藏得很深」的密码,其实一直都躺在明处。

Git 提交历史泄露——最容易被忽视的「案发现场」

我们先看一个真实场景。

你写代码写到半夜,本地数据库连不上。你直接改了 config.py,把密码写进去,git add . && git commit -m "fix db"。

第二天你意识到不对,删掉了密码行,又提交了一次。

——你觉得秘密保住了?

实际上,它永远留在了 Git 历史里。任何人把仓库 clone 下来,执行 git log -p 就能看到你的数据库密码。包括所有历史分支、标签。

⚠️ 核心问题:Git 的 commit 对象是 immutable(不可变的)。你无法通过「再提交一次」擦除历史里的敏感信息。只有 git filter-branch 或 BFG Repo-Cleaner 这种强制重写历史的方式才能真正清理,但那又是一场噩梦——所有协作者都需要重新 clone。

我之前帮一个团队做安全审计,发现他们某个微服务的 .git 目录下,躺着一个裸的数据库密码。他们说「那个密码早换了啊」。我说:「你换了不等于历史里没有。任何拿到你仓库权限的人,都能跑回去看两年前你每一个 commit 的完整 diff。」

💡 典型案例:Uber 2016 年数据泄露事件,攻击者就是从 GitHub 的私有仓库里找到了 AWS 凭据,直接拖走了 5700 万用户数据。他们用的不是 0day,就是一条 git log -p 命令。

git 历史泄露的常见形式

泄露方式 典型场景 危害等级
.git 目录暴露 运维把 .git 一起部署到生产服务器 🔴 极高
历史提交明文密码 调试阶段直接写死在代码里并提交 🔴 极高
配置文件被跟踪 .env / config.yaml 被 add 到 Git 后补加 .gitignore 🟠 高
分支/标签未清理 功能分支包含 secrets,合并后未删除远程分支 🟠 高
commit message 泄露 在描述里写了密码或 🟡 中

环境变量泄露——你以为的保护,其实到处是漏洞

很多人觉得「只要我不把密码写进代码,放到环境变量里就安全了」。

这个想法本身没错,但落地时漏洞百出。

我见过最离谱的一次:某团队把数据库连接串直接写在 ~/.bashrc 里,然后整个 /home 目录权限设置成 755 —— 任何能登录到这台机器的普通用户,都能 cat ~/some_user/.bashrc 拿到所有环境的数据库密码。

💡 避坑指南:环境变量 ≠ 安全变量。它只是「不在代码仓库明文」而已,一旦运行时的进程内存、日志、core dump、子进程继承等环节出问题,secrets 照样裸奔。

环境变量泄露的几种典型途径

  • 进程列表暴露:ps aux 或 /proc/<pid>/environ 可以被其他进程读取。特别是在共享主机或容器中,这是重灾区。
  • 日志/错误输出:应用启动时打印环境变量到 stdout;异常崩溃时 dump 全部环境变量到日志文件。
  • 子进程继承:父进程设置了环境变量,它 fork 出的子进程全部能访问。如果你不小心 exec 了一个脚本,密码就传过去了。
  • 调试终端记录:export SECRET=xxx 打在终端里,会被记录到 ~/.bash_history 或 ~/.zsh_history 中。
  • CI/CD 日志:Jenkins / GitLab CI / GitHub Actions 的构建日志中经常出现环境变量原文打印。
# 真实案例:某 SaaS 公司的 CI 日志里打印了 AWS_SECRET_ACCESS_KEY
# 他们用了 echo 来调试环境变量,没关掉 debug 模式
$ echo "Deploying with key: $AWS_SECRET_ACCESS_KEY"
Deploying with key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
⚠️ 注意:CI/CD 日志往往是公开的,或者对团队内所有人可见。只要一个人不小心把日志链接发出去,你的云资源就危险了。

日志与配置文件——泄露的重灾区

除了 Git 和环境变量,还有两个「裸奔」重灾区:日志文件和配置文件。

日志中的 secrets

  • 请求参数里带着密码或 ,被完整的 access log 记录
  • 应用 debug 日志里打印了 SQL 连接字符串
  • 错误堆栈中包含环境变量或配置信息
  • 审计日志里记录了明文操作凭证

配置文件中的 secrets

  • application.yml / config.json 中的明文密码
  • Dockerfile 中 ENV 指令写了秘密值(会被分层缓存)
  • docker-compose.yml 中的环境变量直接填值
  • k8s ConfigMap 中的明文 (而不是使用 Secret 资源)
  • .npmrc / .pypirc 中的仓库认证

我曾经接手一个项目,所有的云服务密码都写在一个 passwords.txt 文件里,放在项目的根目录。这个文件被 .gitignore 排除了,但部署脚本把它复制到了镜像里。嗯,Docker 镜像推到了公开的 Docker Hub 上。任何人都能 pull 那个镜像,然后 docker run --entrypoint cat 读出所有密码。

一张图看清明文 Secrets 的泄露路径

下面这张图总结了「明文 Secrets 从哪来、到哪去、怎么被拿到」。你可以把它当成一份自查清单。

明文 Secrets 泄露路径全景图 明文 Secrets 泄露路径全景图 源代码 / 配置文件 环境变量 / .env CI/CD 构建脚本 Docker 镜像 / 容器 Git 提交历史 进程内存/环境 CI/CD 日志 镜像分层 / 历史 仓库克隆/分支 子进程/日志 日志聚合/ELK 公开仓库/Registry 攻击者获取明文 Secrets 拖库、横向移动、权限提升 数据泄露 / 服务接管 / 勒索 / 合规罚款 ⚠️ 核心建议 从源头禁止明文 secrets 进入 任何存储、日志、镜像层面

总结一下:为什么明文 Secrets 是「底线问题」

这章讲的不是高级漏洞。不是 SQL 注入,不是 RCE,不是复杂的加密算法破解。

就是最基础的「不该出现的地方出现了」。但偏偏是这种低级问题,每年让无数公司付出惨痛代价。

📋 高频泄露点自查清单
  • ☐ 所有代码仓库(包括私有库)中是否扫描过历史 commit?
  • ☐ CI/CD 日志是否关掉了 debug 输出和秘密回显?
  • ☐ Docker 镜像中是否残留 .env 或 config 文件?
  • ☐ 生产环境日志是否对 secrets 字段做了 mask?
  • ☐ 环境变量是否只在进程启动时注入,且对子进程做最小化传递?

记住一件事:秘密一旦以明文形式出现在磁盘、内存或网络包中,它就不再是秘密了。