最近,有位读者私信我说,他们公司的项目中配置的数据库密码没有加密,编译打包后的项目被人反编译了,从项目中成功获取到数据库的账号和密码,进一步登录数据库获取了相关的数据,并对数据库进行了破坏。

虽然这次事故影响的范围不大,但是这足以说明很多公司对于项目的安全性问题重视程度不够。

 

数据泄露缘由

由于Java项目的特殊性,打包后的项目如果没有做代码混淆,配置文件中的重要配置信息没有做加密处理的话,一旦打包的程序被反编译后,很容易获得这些敏感信息,进一步对项目或者系统造成一定的损害。

所以,无论是公司层面还是开发者个人,都需要对项目的安全性有所重视。

今天,我们就一起来聊聊如何在项目中加密数据库密码,尽量保证数据库密码的安全性。本文中,我使用的数据库连接池是阿里开源的Druid。

 

数据库密码加密

配置数据库连接池

这里,我就简单的使用xml配置进行演示,当然小伙伴们也可以使用Spring注解方式,或者使用SpringBoot进行配置。

<!--数据源加密操作-->
<bean id="dbPasswordCallback" class="com.binghe.dbsource.DBPasswordCallback" lazy-init="true"/><bean id="statFilter" class="com.alibaba.druid.filter.stat.StatFilter" lazy-init="true"><property name="logSlowSql" value="true"/><property name="mergeSql" value="true"/></bean>
<!-- 数据库连接 -->
<bean id="readDataSource" class="com.alibaba.druid.pool.DruidDataSource"destroy-method="close" init-method="init" lazy-init="true"><property name="driverClassName" value="${driver}"/><property name="url" value="${url1}"/><property name="username" value="${username}"/><property name="password" value="${password}"/><!-- 初始化连接大小 --><property name="initialSize" value="${initialSize}"/><!-- 连接池最大数量 --><property name="maxActive" value="${maxActive}"/><!-- 连接池最小空闲 --><property name="minIdle" value="${minIdle}"/><!-- 获取连接最大等待时间 --><property name="maxWait" value="${maxWait}"/><!-- --><property name="defaultReadOnly" value="true"/><property name="proxyFilters"><list><ref bean="statFilter"/></list></property><property name="filters" value="${druid.filters}"/><property name="connectionProperties" value="password=${password}"/><property name="passwordCallback" ref="dbPasswordCallback"/><property name="testWhileIdle" value="true"/><property name="testOnBorrow" value="false"/><property name="testOnReturn" value="false"/><property name="validationQuery" value="SELECT 'x'"/><property name="timeBetweenLogStatsMillis" value="60000"/><!-- 配置一个连接在池中最小生存的时间,单位是毫秒 --><property name="minEvictableIdleTimeMillis" value="${minEvictableIdleTimeMillis}"/><!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 --><property name="timeBetweenEvictionRunsMillis" value="${timeBetweenEvictionRunsMillis}"/>
</bean>

其中要注意的是:我在配置文件中进行了如下配置。

<bean id="dbPasswordCallback" class="com.binghe.dbsource.DBPasswordCallback" lazy-init="true"/><property name="connectionProperties" value="password=${password}"/>
<property name="passwordCallback" ref="dbPasswordCallback"/>

生成RSA密钥

使用RSA公钥和私钥,生成一对公钥和私钥的工具类如下所示。

package com.binghe.crypto.rsa;
import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.util.HashMap;
import java.util.Map;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
/*** 算法工具类* @author binghe*/
public class RSAKeysUtil {public static final String KEY_ALGORITHM = "RSA";public static final String SIGNATURE_ALGORITHM = "MD5withRSA";private static final String PUBLIC_KEY = "RSAPublicKey";private static final String PRIVATE_KEY = "RSAPrivateKey";public static void main(String[] args) {Map<String, Object> keyMap;try {keyMap = initKey();String publicKey = getPublicKey(keyMap);System.out.println(publicKey);String privateKey = getPrivateKey(keyMap);System.out.println(privateKey);} catch (Exception e) {e.printStackTrace();}}public static String getPublicKey(Map<String, Object> keyMap) throws Exception {Key key = (Key) keyMap.get(PUBLIC_KEY);byte[] publicKey = key.getEncoded();return encryptBASE64(key.getEncoded());}public static String getPrivateKey(Map<String, Object> keyMap) throws Exception {Key key = (Key) keyMap.get(PRIVATE_KEY);byte[] privateKey = key.getEncoded();return encryptBASE64(key.getEncoded());}public static byte[] decryptBASE64(String key) throws Exception {return (new BASE64Decoder()).decodeBuffer(key);}public static String encryptBASE64(byte[] key) throws Exception {return (new BASE64Encoder()).encodeBuffer(key);}public static Map<String, Object> initKey() throws Exception {KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);keyPairGen.initialize(1024);KeyPair keyPair = keyPairGen.generateKeyPair();RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();Map<String, Object> keyMap = new HashMap<String, Object>(2);keyMap.put(PUBLIC_KEY, publicKey);keyMap.put(PRIVATE_KEY, privateKey);return keyMap;}
}

运行这个类,输出的结果如下:

在输出的结果信息中,上边是公钥下边是私钥。

对密码进行加密

使用私钥对明文密码进行加密,示例代码如下所示。

package com.binghe.dbsource.demo;
import com.alibaba.druid.filter.config.ConfigTools;
/*** 使用密钥加密数据库密码的代码示例* @author binghe*/
public class ConfigToolsDemo {/*** 私钥对数据进行加密*/private static final String PRIVATE_KEY_STRING = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKtq3IJP5idDXZjML6I8HTAl0htWZSOO43LhZ/+stsIG50WsuW0UJ2vdrEtjvTEfJxP6N1VNrbsF9Lrsp6A4AyUwx00ZUueTlbUaX60134Di0IdQ3C4RTt5mPIbF3hUKers8csltgYR4fByvR3Eq4lt+jAolVHKmyzufukH3d3vJAgMBAAECgYBXiyW+r4t9NdxRMsaI9mZ5tncNWxwgAtOKUi/I1a4ofVoTrVitqoNPhVB+2BtBQQW2IC2uNROq1incZQxeuPxxZJgz1lnnZyHvDE3wuMZAGTcalID+5xBZ2j6fBtDnxbfIL/tIfGJrX+0mUXP2LIo242yQIlzr7RV60iuE2Ms54QJBAOqE0ycvztfxubqBWO7l8PsS3qDUv9lLBBO/Q8I+qVl4tzh+SD/13BqLuaj9eWPGPyml+faWtbmuQgBqauT23l0CQQC7HmMC0CgZS6taQxmPkXzw0XhxZ7tBZeLWl87hqc2S79P0BPX9kPukiC4LpA5xyz0CZ5azJXd2EwRsxF32GERdAkASEi4bJOnxZeUD5BewQPOyxR92kS4/VjJ4OxLDkwSFqnGj3sc+dnmBaibiSLXj5FDVqr56K97Q8gaP9aNLBWLZAkEAjwGnPBQoQUTinaZgl6fibA47VbiolU+v8L+u3iqvMVhXjcxo0DUJDXMCdeUZIQDqDLdsplfBGB1qqVHeWeGsBQJAXGNe2I510WLjMdn+olhi5ZjMr4F4oiF8TAE1Uu74FWn0sc418E7ScgXPCgpGVK0QaXo2wtDeMIoxJwm9Zh8oyg==";public static void main(String[] args) throws Exception {//密码明文,也就是数据库的密码String plainText = "root";System.out.printf(ConfigTools.encrypt(PRIVATE_KEY_STRING, plainText));}
}

运行上述代码示例,结果如下所示。

然后将数据库配置的链接密码改为这个输出结果如下:

jdbc.username=root
jdbc.password=EA9kJ8NMV8zcb5AeLKzAsL/8F1ructRjrqs69zM70BwDyeMtxuEDEVe9CBeRgZ+qEUAshhWGEDk9ay3TLLKrf2AOE3VBn+w8+EfUIEXFy8u3jYViHeV8yc8Z7rghdFShhd/IJbjqbsro1YtB9pHrl4EpbCqp7RM2rZR/wJ0WN48=

编写解析数据库密码的类

package com.binghe.dbsource;
import java.util.Properties;
import com.alibaba.druid.filter.config.ConfigTools;
import com.alibaba.druid.util.DruidPasswordCallback;
/*** 数据库密码回调* @author binghe*/
public class DBPasswordCallback extends DruidPasswordCallback {private static final long serialVersionUID = -4601105662788634420L;/*** password的属性*/private static final String DB_PWD = "password";/*** 数据对应的公钥*/public static final String PUBLIC_KEY_STRING = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCratyCT+YnQ12YzC+iPB0wJdIbVmUjjuNy4Wf/rLbCBudFrLltFCdr3axLY70xHycT+jdVTa27BfS67KegOAMlMMdNGVLnk5W1Gl+tNd+A4tCHUNwuEU7eZjyGxd4VCnq7PHLJbYGEeHwcr0dxKuJbfowKJVRypss7n7pB93d7yQIDAQAB";@Overridepublic void setProperties(Properties properties) {super.setProperties(properties);String pwd = properties.getProperty(DB_PWD);if (pwd != null && !"".equals(pwd.trim())) {try {//这里的password是将jdbc.properties配置得到的密码进行解密之后的值//所以这里的代码是将密码进行解密//TODO 将pwd进行解密;String password = ConfigTools.decrypt(PUBLIC_KEY_STRING, pwd); setPassword(password.toCharArray());} catch (Exception e) {setPassword(pwd.toCharArray());}}}
}

这里DBPasswordCallback类,就是在配置文件中配置的DBPasswordCallback类,如下所示。

<bean id="dbPasswordCallback" class="com.binghe.dbsource.DBPasswordCallback" lazy-init="true"/>

其中PasswordCallback是javax.security.auth.callback包下面的,底层安全服务实例化一个 PasswordCallback 并将其传递给 CallbackHandler 的 handle 方法,以获取密码信息。

当然,除了使用上述的方式,自己也可以对应一套加解密方法,只需要将 DBPasswordCallback的 String password = ConfigTools.decrypt(PUBLIC_KEY_STRING, pwd); 替换即可。

另外,在编写解析数据库密码的类时,除了可以继承阿里巴巴开源的Druid框架中的DruidPasswordCallback类外,还可以直接继承自Spring提供的PropertyPlaceholderConfigurer类,如下所示。

public class DecryptPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer{/*** 重写父类方法,解密指定属性名对应的属性值*/@Overrideprotected String convertProperty(String propertyName,String propertyValue){if(isEncryptPropertyVal(propertyName)){return DesUtils.getDecryptString(propertyValue);//调用解密方法}else{return propertyValue;}}/*** 判断属性值是否需要解密,这里我约定需要解密的属性名用encrypt开头*/private boolean isEncryptPropertyVal(String propertyName){if(propertyName.startsWith("encrypt")){return true;}else{return false;}}
}

此时,就需要将xml文件中的如下配置

<bean id="dbPasswordCallback" class="com.binghe.dbsource.DBPasswordCallback" lazy-init="true"/>

修改为下面的配置。

<bean id="dbPasswordCallback" class="com.binghe.dbsource.DecryptPropertyPlaceholderConfigurer" lazy-init="true"/>

到此,在项目中对数据库密码进行加密和解析的整个过程就完成了。

有道无术,术可成;有术无道,止于术

欢迎大家关注Java之道公众号

好文章,我在看❤️

项目配置不当引发了数据泄露,人已裂开!!相关推荐

  1. 项目配置不当引发了数据泄露,人已裂开!!(建议收藏)

    大家好,我是冰河~~ 最近,有位读者私信我说,他们公司的项目中配置的数据库密码没有加密,编译打包后的项目被人反编译了,从项目中成功获取到数据库的账号和密码,进一步登录数据库获取了相关的数据,并对数据库 ...

  2. Kramdown 配置不当引发 GitHub Pages 多个 RCE,得 $2.5万($6.1万系列之二)

     聚焦源代码安全,网罗国内外最新资讯! 编译:奇安信代码卫士团队 我一直都在盯着 GitHub 企业版会何时修复之前报的漏洞.结果发现GitHub 还修复了 Kramdown 中的一个严重漏洞. 该漏 ...

  3. php 扩展 suhosin 配置不当引发的报错及其解决方法

    1. /var/log/messages 频繁报错: Jul 24 03:27:04 localhost suhosin[9115]: ALERT - script tried to increase ...

  4. 微软低代码工具 Power Apps 配置不当,暴露3800万条数据记录

     聚焦源代码安全,网罗国内外最新资讯! 编译:代码卫士 Upguard 研究院称,由于微软 Power Apps 默认配置安全性薄弱,敏感数据如 COVID-19 打疫苗情况.社保号码和邮件地址遭泄露 ...

  5. pycharm如何更改python项目环境_如何用Pycharm打开已有项目配置python环境

    如何用Pycharm打开已有项目配置python环境 发布时间:2020-07-06 11:06:30 来源:亿速云 阅读:175 作者:清晨 这篇文章将为大家详细讲解有关如何用Pycharm打开已有 ...

  6. Redis配置不当可导致服务器被控制,已有多个网站受到影响 #通用程序安全预警#...

    文章出自:http://news.wooyun.org/6e6c384f2f613661377257644b346c6f75446f4c77413d3d 符合预警中"Redis服务配置不当& ...

  7. [原创] HBuildX,微信小程序模拟器报错(如若已在管理后台更新域名配置,请刷新项目配置后重新编译项目)

    今天在学习使用uniapp,开发微信小程序时第三方登录时,HBuild编辑器报了一个错误,经过定位分析,确定代码是没有问题的,报错如下 如若已在管理后台更新域名配置,请刷新项目配置后重新编译项目 经过 ...

  8. 在配置spring-boot的yml文件中的项目路径时出现横划线表明已过时

    在配置spring-boot的yml文件中的项目路径时出现横划线表明已过时 换成server.servlet.context-path 即可

  9. 记一次CentOS7因Redis配置不当导致被Root提权沦为矿机修复过程

    未曾想过,那些年影视剧中黑客们的精彩桥段,竟在2020这个充满魔幻的年份,变成了现实. 前几日傍晚突然收到了来自阿里云安全中心的提醒,服务器疑似受到攻击了.想不到我那用作学习的机器,有朝一日竟然沦为矿 ...

最新文章

  1. 非阻塞socket的连接
  2. python快速编程入门黑马-500G 史上最全的JAVA全套教学视频网盘分享
  3. 可以节约很多代码的几个正则表达式
  4. SAP Spartacus CmsComponentConnector
  5. 【转】使用Apache Kylin搭建企业级开源大数据分析平台
  6. input输入框的input事件和change事件
  7. Socket实现Android客户端与服务器的通信
  8. 轻量级Java持久化框架,Hibernate完美助手,Minidao 1.6.2版本发布
  9. IT技术人需要具备哪些才能成功
  10. 服务器ghost备份后无法进入系统还原,ghost系统备份后的恢复方法
  11. 支付系统中,账户体系的设计与记账处理
  12. 四大行业晋身今秋求贤大户 游戏人才缺口奇大
  13. 用引流脚本有什么好处,引流脚本是什么意思呢
  14. 计算机怎么会自动开机,电脑怎么设置为通电就自动开机
  15. 微信PC端不显示头像和表情怎么解决
  16. node mysql timeout_Error: Handshake inactivity timeout in Node.js MYSQL module
  17. ubuntu远程访问win7登录后语言为繁体字的解决办法
  18. Qt音视频开发04-保存音频文件(pcm/wav/aac)
  19. kafka redis vs 发布订阅_Redis、Kafka或RabbitMQ,哪个更和微服务更般配?
  20. 【区块链与密码学】第9-4讲:基于PKI的群签名算法 I

热门文章

  1. java aes pbe_JAVA对称加密算法PBE定义与用法实例分析
  2. python豆瓣mysql_python操作mysql
  3. sift算法_单应性Homograph估计:从传统算法到深度学习
  4. 操作系统之I/O管理:1、I/O控制器、I/O控制方式(程序直接控制方式、中断驱动方式、DMA方式、通道控制方式)
  5. 数据结构之排序算法:并归排序
  6. 推动Windows的限制:USER和GDI对象 - 第1部分
  7. linux shell if -a 到-z参数含义
  8. USACO-Section2.1 Hamming Codes(深度优先搜索)
  9. Python 卸载python
  10. Kali中firefox浏览器设置中文