老炮踩坑录 · S03 · 跨项目联动系列
基于四个真实仓库源码分析四种密钥入库姿势,逐条附行号与整改动作
关键词:密钥入库 · Nacos · license.xml · MD5 · JWT secret
👋 欢迎阅读
🏠个人主页:知守观
📘我的专栏: 老炮踩坑录
💻当前内容:密钥入库考古合集
引言
单篇写密钥入库,一个仓库一件事,写完总觉得少点什么。这类问题单独看都小,小到每次 review 都有人放它过去。攒在一起看,才能看出一个共性:密钥进 Git 这件事,几乎没有一次是"不小心",每次都是当时场景下的合理选择——本地调试图方便、脚手架默认就这样、种子脚本总要有个初始密码。
这篇把四个仓库的四种入库姿势摆在一起看。素材来自我维护过的四个真实企业项目,每个案例的行号都是我在本地仓库里核实过的。
四个案例按泄露后果从轻到重排,最后给一张整改对照表。
姿势一:商业授权文件跟着源码进仓库
基础档案项目,PDF 功能用了 Aspose Words。这是个商业库,授权文件长这样:
base-archives-web/src/main/resources/license.xml
它躺在 resources 目录里,跟着源码一起提交、一起打包、一起进 Git 历史。
license.xml 是花钱买的商业授权。入库带来的问题有两层:
第一层是授权泄露:任何拿到仓库访问权的人,顺手就拿到了一份正版授权,拷出去就能在别的项目里用,Aspose 的授权协议是按组织和部署范围算的,这种用法已经越界。
第二层更实际:公司如果因为这个被审计或追溯,仓库历史就是证据,删都删不干净,git filter-branch 也救不了已经 fork 出去的副本。
正确的做法不复杂:授权文件不进仓库,放构建机或部署环境的固定路径,启动时从环境变量读路径。Maven 打包阶段不引用它,本地的放 .gitignore忽略该文件。
姿势二:配置中心的生产密码,明文写在本地配置里
同一个仓库,application-local.yml 第 10 行:
nacos:
config:
password: brg@!526【已脱敏】
本地开发的 profile,连的是生产 Nacos。也就是说,这行明文密码是生产配置中心的钥匙。Nacos 里存着什么?数据源账号、Redis 密码、各种第三方密钥。拿到这把钥匙,等于拿到所有配置的读取权。
写这行配置的人图的是本地调试方便,连上生产 Nacos 就能拉到真实配置,省得本地再维护一份。动机完全能理解,结果也完全不冤枉:本地 profile 文件是仓库的一部分,密码跟着进了 Git。
这个案例值得单独讲的点在于它的隐蔽性。扫描工具盯 application.yml、application-prod.yml 盯得紧,application-local.yml 这种"本地专用"的文件常常被当成安全区。仓库里没有安全区,进了 Git 的每一行都是公开信息,区别只在什么时候被人翻出来。
姿势三:数据源密码、默认密码、MD5 值,一个项目三重入库
金融方向的协作金融项目,这一个仓库贡献了三种姿势,从轻到重:
第一重,application.yml 第 12 到 15 行:
datasource:
# MySQL 5,端口 3306;如密码不同请修改 password
url: jdbc:mysql://localhost:3306/abcdb?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true&allowMultiQueries=true&connectionCollation=utf8mb4_general_ci
username: root
password: root
数据源密码 root 明文,第 12 行还有一行注释:"如密码不同请修改 password"。这行注释的作用是告诉拿到仓库的人:密码就在这里,改这里。全项目只有这一份 yml,没有 profile 分离,测试生产共用。连接串里还带着 allowMultiQueries=true,允许一次请求堆叠多条 SQL,SQL 注入的破坏面被这个参数直接放大。
第二重,SQL 种子文件 sql/xiezuojinrong.sql 第 7 行:
-- 默认账号:admin / 123456(超级管理员)、user / 123456(普通操作员)
默认账号和默认密码以注释形式写死在初始化脚本里。这个脚本会跟着项目交付、跟着部署文档流转。
第三重,同一个 SQL 文件第 334 行:
-- 操作员(密码均为 123456 的 MD5:e10adc3949ba59abbe56e057f20f883e)
e10adc3949ba59abbe56e057f20f883e 这串值被直接写进种子数据。MD5 不加盐,123456 的 MD5 值在任何彩虹表里都是第一页的内容,拿到这串值等于拿到明文。注释还贴心地把对应关系写清楚了,省去了反查的步骤。
一个项目里,配置、脚本、种子数据三条线全部失守。单看每一处都能用"内部系统"解释过去,合在一起看,这个系统的口令体系从仓库层面就是敞开的。
姿势四:脚手架自带的 JWT 密钥,原样沿用
/src/main/resources/application.yml 第 64 行:
jwt:
secret: f4e2e52034***************
这意味着两件事。其一,所有直接部署这套脚手架的系统,JWT 签名密钥是同一个,伪造任意用户的 token 没有门槛。其二,这个密钥从脚手架发布那天起就固定了,用这套代码的人里,改了它的比例,我不敢估。
问题出在直接拿脚手架当生产底座的场景:部署的人默认"框架自带的应该是安全的",这个假设在密钥这件事上不成立。
整改对照表
自查打卡:在你仓库根目录跑一句
git log -p --all -S "password" -- "*.yml" | grep -i "password:" | head -20,数数跳出来几条明文。数字打在评论区,我看看谁的仓库最干净。
老炮点评
四种姿势摆在一起,能看出一个规律:没有一处是攻击者塞进来的,全是开发过程中的人图方便放进去的。授权文件要打包所以放 resources,本地连生产调试图省事所以写了密码,种子脚本得有初始账号所以写了 123456,脚手架要开箱即用所以密钥固定。每一个"当时合理"的决定,都在仓库里留下了永久记录。
Git 的记忆比人好。代码合进去那天没人觉得有事,三年后翻出来,每一行都还在。入库检查这件事靠自觉是靠不住的,pre-commit 钩子扫密钥、CI 里跑一遍 gitleaks,这类工具十来分钟就能接上,比任何一条团队规范都管用。
合规层面我只说一句:金融和政企项目里,"密码是否入库" 已经是审计的固定检查项,被查到不是技术问题,是整改通知单。
最后问一句:你现在的仓库里,
git log -S "password"能搜出东西吗?搜出来的是历史遗留还是上周刚提交的?评论区聊聊,看看大家是怎么做脱敏的。
如果本文对你有帮助,欢迎:
👍 点赞 | ⭐ 收藏 | 👤 关注 | 💬 留言
欢迎留言探讨。你的每一次互动,都是我继续更新的动力🚀
下期预告:《FastJSON 巡检记:同一个依赖坑了五个项目》
跨项目联动系列继续。FastJSON 的 autoType 漏洞修了这么多年,我把手边五个项目的依赖版本逐个翻出来对照,同一个组件,五种版本,五种风险敞口。谁的版本停在漏洞区间里,一张表看清楚。
我是老炮,18 年 Java 老兵,仍在一线。关注「Java老炮踩坑录」,真实项目复盘,让你少走弯路。