资讯动态

Druid密码加密实战:ConfigTools+RSA让数据库密码不再明文裸奔

发布时间:2026/10/21 14:39:19 来源:尧图企业网站定制
数据库密码明文写在配置文件里然后整个仓库一起推到远端这事儿我在不少项目里见过甚至有些人连生产库的密码也是这么裸着的。一旦仓库的访问权限没控制好或者代码包被别人拿走了数据库就等于直接送给对方连爆破都省了。Druid 其实自带了一套成熟的 SQL 密码加密方案配置起来一点都不复杂核心就是官方提供的 ConfigTools 工具配合 RSA 非对称加密。这篇文章把我实际配置的过程、背后的原理、还有踩过的坑完整捋一遍照着做基本十分钟之内就能让配置文件里的密码变成一串密文。1. 为什么数据库密码要加密一个配置文件的生死存亡1.1 明文密码就像一个公开的钥匙盒先聊一个最基本的场景Spring Boot 项目里我们习惯把数据源配置放在 application.properties 或者 application.yml 里最常见的就是这种写法。spring.datasource.usernameroot spring.datasource.password123456 spring.datasource.urljdbc:mysql://10.0.0.5:3306/prod_db写的时候图省事真到了出问题的时候才知道痛。之前我接手过一个项目git 仓库设了公开权限某天在做代码扫描的时候发现整个数据库连接串、账号、密码就在一个老版本的配置提交历史里躺着。虽然时间已经过去很久密码早就换过了但在那段时间里任何拿到仓库的人都能直接连库。最可怕的不是密码本身而是你根本不知道谁看过也不知道数据有没有被悄悄拉走过。你说那我把数据库 IP 设成内网地址不就行了吗很多攻击是发生在内网横向渗透阶段的一旦攻击者进入内网环境扫描数据库端口、用配置里捞出来的账号密码直接连接是非常常规的操作。所以把账号密码从配置文件里彻底解除出来不是安全部门找茬而是运维和开发的基本素养。1.2 RSA 公私钥方案Druid 官方加密机制的选型逻辑Druid 的 ConfigTools 工具走的是 RSA 非对称加密路线。所谓非对称就是加密和解密用的是不同的钥匙公钥负责把明文密码加密成密文私钥负责在运行时把密文还原成明文。公钥可以随便丢在配置文件里因为公钥本身无法反向推导出私钥别人拿到你的公钥和密文在没有私钥的情况下也是解不开的。对称加密用起来虽然简单但问题也很明显加密和解密用的是同一把密钥那这把密钥放在哪里放在配置文件里等于没加密放在环境变量里又得保证所有部署节点同步。非对称加密的好处就体现出来了公钥放仓库里不心疼私钥放在部署机、环境变量或者配置中心里access 严格控制即使仓库整个泄露数据库的真实密码仍然是安全的。Druid 在连接池初始化的时候会读取 connection-properties 里的解密开关和公钥信息再通过配置的私钥对密文进行解密最终拿到明文去和数据库建立连接。这一整套流程对业务代码完全透明你不需要改一行 DAO 代码数据源照常注入连接照常创建。2. 实操十分钟跑通 Druid 的 SQL 密码加密2.1 准备工作确认 Druid 版本和依赖ConfigTools 的密码加密能力在比较新的版本里才稳定建议至少使用 1.1.6 以上版本当前推荐直接用 1.2.x 的最新版。版本太老的话某些配置项可能不兼容出了问题还不好查。如果你的项目已经在用 Druid 了先确认 pom.xml 或者 gradle 里的版本号。Maven 坐标示例dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-starter/artifactId version1.2.8/version /dependency如果你用的是原生 druid 包而非 starter也是一样的道理只要 jar 里有com.alibaba.druid.filter.config.ConfigTools这个类就可以继续往下走。2.2 通过 ConfigTools 生成密钥对和密文这一步是整个加密的核心前置操作。Druid 官方提供了一个现成的工具类不需要你自己写 RSA 生成逻辑。用命令行直接执行java -cp druid-1.2.8.jar com.alibaba.druid.filter.config.ConfigTools your_plain_password把your_plain_password替换成你真实的数据库密码。执行之后终端会输出三行关键内容privateKey:MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL... publicKey:MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKJOxG... password:A9f4jf8sdf8sdf7...这里需要特别注意password那一串才是用来替换配置文件里明文密码的密文publicKey要放到配置文件的connection-properties里privateKey不要写进会被提交到仓库的任何文件中要放到部署环境的安全位置。如果你的项目不方便执行这种命令行方式也可以写个临时的 Java main 方法直接调用 ConfigTools 的 main 入口效果完全一样。2.3 把密文和公钥写进配置项以 Spring Boot 项目为例修改 application.properties 中的数据源配置spring.datasource.druid.usernameroot spring.datasource.druid.password上面输出的密文password spring.datasource.druid.connection-propertiesconfig.decrypttrue;config.decrypt.key上面输出的publicKey spring.datasource.druid.filtersconfig关键点在于这三项password一定要换成工具生成的密文不要保留明文。connection-properties里用分号分隔两个参数config.decrypttrue表示开启数据库密码解密config.decrypt.key公钥指定公钥内容。filtersconfig必须包含 config这是触发解密功能的过滤器漏掉它启动阶段就会报错。如果你不是在 Spring Boot 环境里而是手动创建 DruidDataSource那配置方式也类似DruidDataSource dataSource new DruidDataSource(); dataSource.setUrl(jdbc:mysql://10.0.0.5:3306/prod_db); dataSource.setUsername(root); dataSource.setPassword(上面输出的密文password); Properties properties new Properties(); properties.setProperty(config.decrypt, true); properties.setProperty(config.decrypt.key, 上面输出的publicKey); dataSource.setConnectProperties(properties); dataSource.setFilters(config);这段代码里的逻辑和 Spring Boot 配置是完全对应的核心就是设置连接属性和 filter。2.4 验证加密是否真正生效配置改完之后不要直接上生产先本地或者测试环境验证一遍。启动应用观察控制台日志和数据库端的连接情况。一个比较直接的验证方法故意把配置里的 password 改成一串错误的密文比如把最后一个字符改掉然后重新启动应用。如果配置生效了应用应该报解密失败或者说获取连接失败而不是直接报 access denied 之类的数据库认证错误。如果还能正常连接数据库说明你的 password 字段里还被某个解析器覆盖成了别的值或者 filter 根本没生效。我习惯的验证流程是先正常启动一次确认业务无异常再故意破坏密文确认启动会失败最后改回正确密文正式发布。这样能确保加密链路是通的而不是自己骗自己。3. 配置背后的细节原理这样写才不容易翻车3.1 filtersconfig 到底是干什么的很多人配置完发现没效果第一反应是公钥不对、密文不对实际上最常犯的错是漏了 filters。Druid 的密码解密不是一个自动发生的过程它需要经过一个叫做 config filter 的过滤器。filters 里没写 configDruid 根本不会去解析 connection-properties 里的解密参数密文就会被当成真实的密码直接拿去连数据库结果必然报错。如果你配置了多个 filter比如统计、日志、慢查询要写在一起并用逗号分隔spring.datasource.druid.filtersstat,config,slf4j注意顺序其实不重要Druid 会按约定加载。但 config 这个必须出现而且要保证你的 filter 列表覆盖了它。3.2 公钥、私钥的正确分工与安全存放生成出来的三串信息很容易让人搞混。我再强调一遍用法因为我见过有人把 privateKey 也写进配置文件的那就等于没加密或者说比加密前还多暴露了一把私钥。公钥publicKey写进 application.properties 的config.decrypt.key可以随代码仓库走可以公开。私钥privateKey绝对不要进仓库也不要写死在代码里。放到部署服务器的 /etc/druid/ 目录、环境变量、或者配置中心的安全项里通过${DRUID_PRIVATE_KEY}之类的占位符引用。密文password替换数据库连接配置里的 password 项。Spring Boot 里可以用环境变量加占位符的方式来引用私钥spring.datasource.druid.connection-propertiesconfig.decrypttrue;config.decrypt.key${DRUID_PUBLIC_KEY}有朋友会问公钥都放配置文件里了岂不是拿到公钥也能通过密文反推出明文RSA 非对称加密在数学上保证了通过公钥无法在合理时间内推导出私钥反过来也一样。所以公钥公开没有实质风险真正要保护的是私钥。3.3 多环境隔离与密钥轮换同一个项目可能有开发、测试、预发、生产四套环境数据库密码各不相同密钥对也应该各不相同。不要让所有环境共享同一把公钥和私钥否则测试环境的密钥泄露了生产环境也得跟着一起换。比较好的实践是每个环境生成独立的密钥对分别维护密文和公钥。开发环境甚至可以直接用明文密码方便本地调试因为开发库风险可控但从测试环境开始启用加密确保部署链路和生产一致。密钥轮换是另一个绕不开的话题。数据库密码一旦换掉需要重新跑一遍 ConfigTools 生成新密文然后用新密文替换配置重启应用。如果公钥私钥也需要轮换顺序上是先用旧私钥确认当前还能正常解密生成新的密钥对基于新公钥加密新密码先把新密文和新公钥配到预发或灰度环境确认没问题生产环境先切一半实例观察连接池和业务指标正常后再全量替换最后清理旧私钥和旧密钥对的备份文件。整个过程其实没有那么复杂但一定要有记录用表格记录每个环境对应的公钥指纹、密文生成时间、上次轮换时间后面排查问题会省很多时间。3.4 其他值得注意的配置细节有些版本的 Druid 支持把公钥放到单独的配置文件里然后通过 key 属性指定路径比如config.decrypt.keyclasspath:druid-key/public.key这种方式的好处是配置文件更干净公钥文件和代码文件分离。但要注意 classpath 路径别写错启动时如果找不到文件同样会报错。另外有些项目里会同时配置多个数据源比如多数据源分库分表场景。每个数据源都要单独设置 connection-properties 和 filters不能只配主数据源就指望所有连接都被加密这个坑比较隐蔽排查起来也容易绕弯路。我曾经在一个多数据源项目里漏配了其中一个库的 config filter结果那个库的密码一直是明文状态后来做库表改造的时候才发现。4. 常见报错与排查实录4.1 启动直接报 invalid public key这类报错一般长这样Caused by: java.security.NoSuchAlgorithmException: invalid public key原因是公钥内容不对。可能情况是复制公钥的时候漏了字符、多了换行或者复制成了 privateKey。解决办法就是重新跑一次 ConfigTools把新生成的公钥完整复制过去注意前后不要有多余空格yaml 文件里尤其容易因为缩进导致字符串被截断。如果公钥本身没放错但还是报错检查一下 connection-properties 的 key 名称。Druid 要求公钥配置项叫config.decrypt.key不能写成publicKey或者decrypt.key一旦名字对不上解密流程拿不到公钥就会报类似错误。4.2 加了 config 之后仍然报密码错误密码错误这个大方向下可以拆成几种情况密文复制错了把 publicKey 当成密文写进 password 字段。这种情况数据库会收到一串长度很长的“密码”连接失败。密文是对的但公钥和私钥不是同一对。比如你用了 A 环境生成的公钥配了 B 环境生成的私钥去解RSA 解密出来的结果完全是乱码连库自然失败。私钥没正确传进去。如果私钥通过环境变量引用先确认 shell 里能不能 echo 出来。有些部署平台的环境变量里有特殊字符会导致私钥内容被截断。排查技巧打开 debug 日志级别观察日志里 Druid 解密阶段有没有输出明显的异常堆栈。如果堆栈里是 Cipher 相关的BadPaddingException那基本就是密钥对不匹配或者密文被改过。4.3 为什么换了一台机器就不行了在本地明明配置得好好的部署到新服务器上就启动失败。九成原因是私钥没有部署到那台机器上。私钥如果放在某个文件路径里比如 /opt/keys/private_key.pem那每台部署机器上都得有这个文件还要保证运行应用的系统用户对这个文件有读取权限。很多环境问题都是权限问题文件存在但应用进程读不了。如果是通过环境变量注入要检查部署平台的环境变量配置是不是在构建阶段被覆盖了。比如同一个镜像在不同的部署环境里用同一套环境变量那密钥就得按环境单独指定。4.4 日志里打印密文和明文还有一个很容易被忽略的安全细节日志打印。有些项目会在启动阶段打印数据源配置或者配置中心拉取配置的时候把整个数据源内容输出到日志里。如果你在 application.yml 里打开过logging.level.com.alibaba.druiddebugDruid 内部连接数据库之前的一些信息可能会被打印出来里面的 password 字段暴露了密文事小如果某段逻辑不小心在解密后打印了明文那风险就大了。建议在排查问题的时候临时开 debug排查完立刻调回 warn 或 info。另外连接池监控页面里如果配置了展示密码之类的功能也要在对外暴露前关掉或者脱敏。4.5 版本升级带来的兼容问题老项目从 Druid 1.0.x 往上升级的时候可能遇到加密参数不识别的问题。1.0.x 版本的 config filter 还不支持后来引入的一些配置格式需要直接升级到 1.1.x 或者 1.2.x 才能正常使用。升级依赖之后建议先跑一遍全量测试特别是多数据源和分页、事务相关的场景因为连接池版本变化可能影响初始化和连接获取逻辑。还有一个不太常见但确实存在的情况项目中同时引用了多个版本的 druid jar 包比如某个公共组件内部自带了一套旧版 druid导致应用实际加载的 ConfigTools 类和预期的不一致。这种包冲突问题可以用依赖树分析或者直接排除传递依赖再显式指定一个版本。4.6 connection-properties 里的特殊字符connection-properties 是字符串形式的多个属性之间用分号分隔。如果公钥里有特殊字符比如加号、斜杠、等号在 yaml 或 properties 文件里可能被解析器处理掉。建议把公钥用双引号包起来spring.datasource.druid.connection-propertiesconfig.decrypttrue;config.decrypt.keyMFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAKJOxG...如果还是有问题把公钥放到外部文件里用路径引用能有效绕开特殊字符问题。在我实际配过的项目里最折腾的一次就是在 Kubernetes 环境里通过 configmap 挂载 properties 文件公钥里的号被环境变量渲染的时候转成了空格导致所有环境全部启动失败。排查这个问题的办法是把配置内容打进镜像之前先 base64 解码看一遍确认特殊字符没有被吞。5. 后面还可以怎么扩展5.1 结合配置中心统一管理密钥用 Nacos 或类似的配置中心来管理数据源配置的话可以把公钥、密文都放到配置中心只有运维人员有权限修改。私钥可以存在每个应用所在机器本地这样配置中心就算被人扒了也拿不到私钥数据源照样安全。配置中心本身就支持版本回滚切换密钥的时候还能快速回退比直接改配置文件要方便得多。5.2 Jasypt 并不是唯一选择做 Java 项目的朋友可能会想到另一个密码加密库 Jasypt它也能对 Spring Boot 配置里的密码进行加密。Jasypt 的优点是使用起来对配置属性几乎无侵入可以把任意配置项的明文变成 cipher 字符串。但 Jasypt 默认的加密方式需要你把密钥secret以明文形式也放在配置文件或者环境变量里它本身不解决密钥管理问题。Druid 自带方案的独特优势是解密动作和连接池绑定得更紧RSA 非对称机制避免了 Jasypt 的对称密钥难题。如果你已经在用 Druid没必要额外引一套 Jasypt直接用 ConfigTools 就好。如果有些密码不是数据库连接用的而是类似 Redis 密码也可以考虑用 Jasypt 单独处理但那就是另一套方案了。5.3 密钥管理和安全审计配置加密这件事本质上不是为了防住百分百的攻击而是把风险控制在一个可控的范围。数据库密码只是一个起点整个配置体系里往往还有消息队列密码、Redis 密码、第三方平台密钥。如果都处于明文状态那安全建设的价值就大打折扣。建议每个项目都建立一张配置加密登记表记录配置项名称、加密方式、密钥存放位置、轮换周期。等到安全审计的时候这一张表就是你最有说服力的整改证据比临时截图强太多了。我个人的体会是Druid 密码加密这个功能第一次配的时候稍微有点绕但真正跑通之后后面所有数据库连接都走同一套流程心理负担会小很多。你不需要记住那一串 RSA 密钥原理的细节只需要知道公钥进配置私钥进环境密文替换密码filters 别漏这四句话能帮你解决八成以上的问题。后面如果遇到了更复杂的场景比如多数据源、Kubernetes 部署、密钥轮换再回到这几个关键细节上查基本都能找到原因。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑