项目配置不当引发了数据泄露,人已裂开!!
最近,有位讀者私信我說,他們公司的項目中配置的數(shù)據(jù)庫密碼沒有加密,編譯打包后的項目被人反編譯了,從項目中成功獲取到數(shù)據(jù)庫的賬號和密碼,進一步登錄數(shù)據(jù)庫獲取了相關(guān)的數(shù)據(jù),并對數(shù)據(jù)庫進行了破壞。
雖然這次事故影響的范圍不大,但是這足以說明很多公司對于項目的安全性問題重視程度不夠。
?
數(shù)據(jù)泄露緣由
由于Java項目的特殊性,打包后的項目如果沒有做代碼混淆,配置文件中的重要配置信息沒有做加密處理的話,一旦打包的程序被反編譯后,很容易獲得這些敏感信息,進一步對項目或者系統(tǒng)造成一定的損害。
所以,無論是公司層面還是開發(fā)者個人,都需要對項目的安全性有所重視。
今天,我們就一起來聊聊如何在項目中加密數(shù)據(jù)庫密碼,盡量保證數(shù)據(jù)庫密碼的安全性。本文中,我使用的數(shù)據(jù)庫連接池是阿里開源的Druid。
?
數(shù)據(jù)庫密碼加密
配置數(shù)據(jù)庫連接池
這里,我就簡單的使用xml配置進行演示,當然小伙伴們也可以使用Spring注解方式,或者使用SpringBoot進行配置。
<!--數(shù)據(jù)源加密操作--> <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> <!--?數(shù)據(jù)庫連接?--> <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}"/><!--?連接池最大數(shù)量?--><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}"/><!--?配置間隔多久才進行一次檢測,檢測需要關(guān)閉的空閑連接,單位是毫秒?--><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;} }運行這個類,輸出的結(jié)果如下:
在輸出的結(jié)果信息中,上邊是公鑰下邊是私鑰。
對密碼進行加密
使用私鑰對明文密碼進行加密,示例代碼如下所示。
package?com.binghe.dbsource.demo; import?com.alibaba.druid.filter.config.ConfigTools; /***?使用密鑰加密數(shù)據(jù)庫密碼的代碼示例*?@author?binghe*/ public?class?ConfigToolsDemo?{/***?私鑰對數(shù)據(jù)進行加密*/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?{//密碼明文,也就是數(shù)據(jù)庫的密碼String?plainText?=?"root";System.out.printf(ConfigTools.encrypt(PRIVATE_KEY_STRING,?plainText));} }運行上述代碼示例,結(jié)果如下所示。
然后將數(shù)據(jù)庫配置的鏈接密碼改為這個輸出結(jié)果如下:
jdbc.username=root jdbc.password=EA9kJ8NMV8zcb5AeLKzAsL/8F1ructRjrqs69zM70BwDyeMtxuEDEVe9CBeRgZ+qEUAshhWGEDk9ay3TLLKrf2AOE3VBn+w8+EfUIEXFy8u3jYViHeV8yc8Z7rghdFShhd/IJbjqbsro1YtB9pHrl4EpbCqp7RM2rZR/wJ0WN48=編寫解析數(shù)據(jù)庫密碼的類
package?com.binghe.dbsource; import?java.util.Properties; import?com.alibaba.druid.filter.config.ConfigTools; import?com.alibaba.druid.util.DruidPasswordCallback; /***?數(shù)據(jù)庫密碼回調(diào)*?@author?binghe*/ public?class?DBPasswordCallback?extends?DruidPasswordCallback?{private?static?final?long?serialVersionUID?=?-4601105662788634420L;/***?password的屬性*/private?static?final?String?DB_PWD?=?"password";/***?數(shù)據(jù)對應(yīng)的公鑰*/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包下面的,底層安全服務(wù)實例化一個 PasswordCallback 并將其傳遞給 CallbackHandler 的 handle 方法,以獲取密碼信息。
當然,除了使用上述的方式,自己也可以對應(yīng)一套加解密方法,只需要將 DBPasswordCallback的 String password = ConfigTools.decrypt(PUBLIC_KEY_STRING, pwd); 替換即可。
另外,在編寫解析數(shù)據(jù)庫密碼的類時,除了可以繼承阿里巴巴開源的Druid框架中的DruidPasswordCallback類外,還可以直接繼承自Spring提供的PropertyPlaceholderConfigurer類,如下所示。
public?class?DecryptPropertyPlaceholderConfigurer?extends?PropertyPlaceholderConfigurer{/***?重寫父類方法,解密指定屬性名對應(yīng)的屬性值*/@Overrideprotected?String?convertProperty(String?propertyName,String?propertyValue){if(isEncryptPropertyVal(propertyName)){return?DesUtils.getDecryptString(propertyValue);//調(diào)用解密方法}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"/>到此,在項目中對數(shù)據(jù)庫密碼進行加密和解析的整個過程就完成了。
有道無術(shù),術(shù)可成;有術(shù)無道,止于術(shù)
歡迎大家關(guān)注Java之道公眾號
好文章,我在看??
總結(jié)
以上是生活随笔為你收集整理的项目配置不当引发了数据泄露,人已裂开!!的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 重磅!GitLab 在中国成立公司极狐,
- 下一篇: [题解](双向bfs)hdu_3085_