数据库口令从代码搬进凭据托管,轮换从半天变成两分钟

发布时间:2026/11/4 12:11:41
数据库口令从代码搬进凭据托管,轮换从半天变成两分钟

数据库口令从代码搬进凭据托管,轮换从半天变成两分钟

上周给一个测试库换口令。改完发现要发版——那串字符在代码里写死了三处:配置类一份、启动脚本一份、连接池初始化一份。逐处改完,还得挨个重启。

漏的地方第二天早上才暴露:服务起不来,日志里一句 access denied。

这件事本身不新鲜,很多人忍着。但它把一个问题摊平了:口令写在代码里,你连「换掉它」这个动作都做不了。做不了就不会去做;不换,那串字符就一直有效。

这次我把它搬进了凭据管理。下面记的是配置过程和我实际改了什么——过程点到为止,重点在后面那张对比。

一、动手之前,先列清楚要什么

挑工具容易,想清楚要什么难。动手之前我列了四条,按轻重排:

第一,换口令不能发版。这是主诉。做不到这条,后面都是空谈。

第二,要知道谁在用。库里只有一个账号app,五个人、三台机器、两个环境都在用它。审计日志里只有app,问不出具体是谁。

第三,出事能立刻作废。半夜发现异常访问,需要的是「一分钟内让这串字符失效」,不是「明天上午走个变更」。

第四,环境之间别共用一份配置。测试、预发、生产共用一份样例,改一处漏一处——我这次就是漏的。

四条里前三条凭据管理能解,第四条得靠流程,工具替不了。这个边界一开始就该认下来,否则后面容易指望错。

二、配置是怎么走的

真配起来是四步,比预想的少,但第一步是我原来没想到的。

第一步,建根凭据。这是整个机制的地基:把数据库的管理员账号托管进来——数据库类型、主机、端口、库名、管理员账号和口令,系统当场验证连接。验证通过,这条根凭据才能用来派生别的。这一步可以勾「开启轮转」,勾了之后管理员口令按周期自动更新。

第二步,建子凭据。类型选「动态」,然后指定几件事:挂在哪个根凭据下、服务类型要和根凭据一致、有效时间多长、最大有效时间多长、权限是继承根凭据还是自定义增删改查、到期前要不要自动续约。

这里有个容易忽略的约束:有效时间不能超过最大有效时间,而最大有效时间是硬上限——子凭据存在的时间超过它之后自动失效,续约也救不回来。

第三步,关联用户。子凭据建完不归任何人。要把用户挂上去,对方才拿得到。

第四步,应用接入。应用侧不再读配置,改成调接口取。取回来的东西两种类型还不一样:静态凭据解出来直接是账号口令,动态凭据解出来是带有效期的一小段 JSON。

静态凭据这一步短得多:不依赖根凭据,一条独立记录,填名称、值、用途和一个加密方式就完了。

三、静态和动态,差的不是有效期

这是我配之前理解错的地方。

我原来以为两者的区别是有效期长短——静态不限期、动态几分钟。真配下去才发现不是:区别在账号从哪来,以及要不要把管理员账号交给系统。

静态凭据里,账号是你自己的。你在数据库里建好一个账号,把口令填进凭据管理,系统负责加密存着、按权限发给该拿的人。它不需要知道怎么连你的库。

动态凭据里,账号是系统替你建的。所以要先把管理员账号(根凭据)交给它,它才能连上库、按需派生临时账号、到期失效。这就是动态凭据必须挂在根凭据下面、服务类型必须跟根凭据一致的原因。

差别决定了选型:

静态凭据动态凭据
前置条件无,独立存在必须先有根凭据(管理员账号)
账号从哪来你自己在库里建系统按根凭据派生
系统的权限只存密文,不碰你的库能动你的库(建号、回收)
有效期没有这个概念有效时间 + 最大有效时间(硬上限)
授权粒度用户,分「查看」「使用」两档用户,权限可继承根凭据或自定义

所以这不是「先进与落后」的选择,是信任边界的选择:愿不愿意把管理员账号交给一个系统。

产品里的动态凭据也不止数据库一种。同一套「根凭据派生」的模型还用在 SSH(OTP 一次性密码和密钥对两种方案)和主机凭据(Windows/Linux 本地账户按周期轮换)上。这次我只跑了数据库这一条路。

代价分开说,不然就是只讲好话。

动态的代价主要不在代码上:交出管理员账号是一次实质的信任转移,不是配置问题;系统要能连上目标库,网络策略得开。应用侧还有一件必须做的事——凭据是有状态的(有效、已过期、已吊销、申请中),到期前可以续约,过期后得重新申请。写成「取一次用到底」,半夜就会挂。

静态的代价更隐晦:它解决「存」和「发」,不解决「换」。谁来轮换、多久轮一次,还是人的事。放进保险箱和定期换锁,是两件事。

四、配好之后,实际变了什么

这部分是我最想记的。功能列表谁都会写,实际变了多少,得自己用一段时间才知道。

链路的长度其实没变,还是那么五步。变的是口令驻留在哪几个环节:改造前它跟着代码走完整条链,改造后只在取用那一下出现,而且自己会过期。这也是为什么「换口令」从一件要排期的事,变成了一个点一下的动作——它不必再碰应用、代码仓库、部署机器这三样。

再展开看细节:

维度之前(写在代码里)之后(凭据托管)
换一次口令改代码 + 提测 + 发版 + 重启,半天起步在凭据上执行轮转,两分钟
紧急作废走一遍发版流程禁用或吊销,立即生效
谁在用只知道账号名授权名单落到具体用户
环境隔离共用一份配置样例每环境一套根凭据与子凭据,各自授权
过期没有这个概念静态靠人轮换,动态按有效时间自动失效
出事排查翻代码找哪几处写死了查这条凭据的取用记录
人员交接口令跟着人走改授权就行,不必把口令告诉谁

表里真正的变化不在「安全」两个字上,在第一行:换口令从「一个项目」变成了「一个动作」。

以前换口令要排期、要等发版窗口、要通知回归。流程这么重,结果就是没人愿意换——一年一次都算勤快。现在两分钟的事,才会真的按季度去做。

安全收益是这件事的副产品。被改掉的是「要不要换」这个判断本身——它不再由成本决定。

还有一条不那么显眼的变化:排查的时候终于有据可查。以前库里出现异常连接,只能对着代码猜是哪台机器、哪个人;现在能顺着授权名单往回追。

五、几条验收判据

真要搬,按这几条过一遍再算完:

#检查项判据
1代码里还有没有硬编码全仓库检索口令片段,命中为 0
2该用哪种类型账号由自己管 → 静态;要按需建号 → 动态
3动态的前置根凭据已验证连通,服务类型与根凭据一致
4两个时间有效时间不超过最大有效时间,且最大有效时间设了上限
5续约策略到期前自动续约开不开,应用侧处理了续约与重新申请两条分支
6归属授权名单非空,且能对应到具体用户
7应用侧缓存缓存时间小于凭据有效期
8数据库侧回收动态凭据要有建号回收的对应动作

第 1 条和第 5 条常挂。前者是因为「就这一次」的临时脚本会沉淀成固定资产;后者是把凭据当静态配置用,取回来就存着不放手——到期了还在用旧口令,表现是夜里开始报连接失败。

收尾

口令该放哪,判断标准就一条:能不能不发版就作废,以及会不会自己过期。写在代码里两条都不满足,托管起来满足第一条,动态凭据两条都满足。

在安当,我们做这一层时有两个取舍,和上面的过程正好对得上。

一是静态凭据的权限分「查看」和「使用」两档。能看不等于能用,看得见也不等于能取到明文。多一档权限就多一次配置,换来的是出了问题能收窄到「谁看过」和「谁用过」两种不同的追责路径。

二是值写进去之后不回读,控制台只给摘要。对拿它当配置中心用的人是限制,但它把「谁能看到明文」从权限配置问题变成了架构问题——不是靠约定没人去看,是结构上看不着。

这两条的代价我们也认:多一档权限要多配一遍,排障时看不到原文得靠取用记录对。我们宁可留着这两处不方便,因为把它们改顺手的代价,是出事那天答不出「谁在用、什么时候开始用的」。

文章作者:安当加密技术负责人