詳解Java中的敏感信息處理
平時(shí)開(kāi)發(fā)中遇到像用戶的手機(jī)號(hào)、姓名、身份證等信息,在傳輸和入庫(kù)節(jié)點(diǎn),有以下常用的解決方案。
前后端傳輸
AES 對(duì)稱加密
對(duì)稱加密的加密和解密密鑰是同一個(gè),把密鑰放在前端,萬(wàn)一被破解,有一定的風(fēng)險(xiǎn),不建議使用。
RSA 非對(duì)稱加密
提前生成一對(duì)公鑰和私鑰。
前端使用公鑰加密,公鑰是公開(kāi)的,后端使用私鑰解密,私鑰放在配置中心不要寫在代碼里,只要后端私鑰不泄露,沒(méi)有任何問(wèn)題。
RSA 加解密速度比較慢,超過(guò) 10KB 的數(shù)據(jù)就要分段,或者使用混合加密。
混合加密
在對(duì)大文件進(jìn)行加密時(shí),只使用 RSA 效率非常低,可以使用 RSA + AES 混合加密方式。
首先跟 RSA 步驟一樣,前端存儲(chǔ)公鑰,后端存儲(chǔ)私鑰。
然后前端隨機(jī)生成 AES 密鑰,使用 RSA 加密 AES 密鑰,再用 AES 加密大文件數(shù)據(jù)。
再把加密后 AES 密鑰和加密后的大文件數(shù)據(jù)一起傳給后端。
后端使用 RSA 解密 AES 密鑰,再用 AES 密鑰解密數(shù)據(jù)。
我這只用加密身份證、手機(jī)號(hào)等數(shù)據(jù),使用 RSA 非對(duì)稱加密即可。
數(shù)據(jù)庫(kù)加密
數(shù)據(jù)庫(kù)加密存儲(chǔ)要考慮到查詢效率和平時(shí)查 case 的便捷性。
MD5 + Salt/SHA + Salt
對(duì)用戶的密碼這種不需要解密的場(chǎng)景,可以用 SHA + Salt 方式加密。
由于彩虹表攻擊(窮舉 MD5 加密前后的數(shù)據(jù),例如網(wǎng)站上的 MD5 破解),MD5 已經(jīng)不那么安全了,尤其是沒(méi)有加鹽的時(shí)候,不再推薦使用。
AES 加密
以手機(jī)號(hào)為例,把手機(jī)號(hào)加密后無(wú)法使用手機(jī)號(hào)明文查詢,加的索引也是密文。
業(yè)務(wù)中會(huì)遇到通過(guò)手機(jī)號(hào)查詢、或批量查詢的場(chǎng)景,比如判斷用戶是否注冊(cè)過(guò)。
推薦使用 AES 加密,密鑰配在配置中心。
封裝額外的查詢接口,比如 getByPhone。
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; @Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> { private static final String SECRET_KEY = "your-encryption-key"; // 替換為你的加密密鑰 /** * 根據(jù)明文手機(jī)號(hào)查詢單個(gè)用戶信息 * * @param plainPhone 明文手機(jī)號(hào) * @return 匹配的用戶信息(數(shù)據(jù)庫(kù)密文解密后攜帶明文手機(jī)號(hào)信息) */ public User getByPhone(String plainPhone) { // 1. 將明文手機(jī)號(hào)加密為密文 String encryptedPhone = encryptPhone(plainPhone); // 2. 構(gòu)造查詢條件,通過(guò)密文匹配 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.eq("encrypted_phone", encryptedPhone); // 3. 查詢并返回結(jié)果 User user = this.getOne(queryWrapper); // 4. 注入明文手機(jī)號(hào)回傳,以便客戶端知道原來(lái)的手機(jī)號(hào) if (user != null) { user.setPlainPhone(plainPhone); // 假設(shè)實(shí)體類 User 里有這個(gè)字段 } return user; } /** * 根據(jù)明文手機(jī)號(hào)列表查詢對(duì)應(yīng)的用戶信息 * * @param plainPhones 明文手機(jī)號(hào)列表 * @return 匹配的用戶信息列表(數(shù)據(jù)庫(kù)密文解密后攜帶明文手機(jī)號(hào)信息) */ public List<User> listByPhoneIn(List<String> plainPhones) { // 1. 將明文手機(jī)號(hào)列表加密為密文列表 List<String> encryptedPhones = plainPhones.stream() .map(this::encryptPhone) // 調(diào)用加密方法 .collect(Collectors.toList()); // 2. 構(gòu)造查詢條件,通過(guò)密文列表匹配 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.in("encrypted_phone", encryptedPhones); // 3. 查詢并返回結(jié)果 List<User> users = this.list(queryWrapper); // 4. 將原始明文手機(jī)號(hào)與對(duì)應(yīng)的密文進(jìn)行綁定,注入到返回結(jié)果中 for (User user : users) { // 找到與當(dāng)前密文匹配的明文手機(jī)號(hào) String plainPhone = plainPhones.get(encryptedPhones.indexOf(user.getEncryptedPhone())); user.setPlainPhone(plainPhone); // 假設(shè)實(shí)體類 User 里有這個(gè)字段 } return users; } /** * 加密手機(jī)號(hào)的方法(對(duì)稱加密,AES) * * @param plainPhone 明文手機(jī)號(hào) * @return 加密密文手機(jī)號(hào) */ private String encryptPhone(String plainPhone) { // 此處省略加密邏輯,可自行實(shí)現(xiàn) AES 加密對(duì)應(yīng)方法 // 假設(shè)這里有一工具類調(diào)用 AES 加密,示例傳入密鑰和數(shù)據(jù)進(jìn)行加密 return AESUtils.encrypt(plainPhone, SECRET_KEY); // 示例為偽代碼,請(qǐng)用實(shí)際工具實(shí)現(xiàn) } }
case 查詢時(shí)可以用數(shù)據(jù)庫(kù) aes 加密查詢。
-- 查詢特定手機(jī)號(hào) SELECT * FROM users WHERE phone_encrypted = AES_ENCRYPT(plainPhone, SECRET_KEY);
不要直接使用 MySQL AES 解密查詢,這樣會(huì)掃全表解密比對(duì),數(shù)據(jù)庫(kù)坐等爆炸。
針對(duì)手機(jī)號(hào)這種特殊場(chǎng)景,還可以根據(jù)業(yè)務(wù)額外存前綴和后綴,方便范圍查詢。
如果對(duì)加密有更高的要求,可以在前后端和入庫(kù)時(shí)設(shè)置版本號(hào),定期修改 RSA 公私鑰和 AES 密鑰。
前端在通過(guò)接口獲取公鑰,后端把公私鑰存在數(shù)據(jù)庫(kù),附上版本號(hào)和生效日期。
后端解密時(shí),根據(jù)前端傳入的版本獲取對(duì)應(yīng)私鑰解密。
入庫(kù)時(shí)把版本號(hào)也存入,解密時(shí)通過(guò)版本號(hào)查詢密鑰再解密。
目前沒(méi)遇到要求這么高的場(chǎng)景,我計(jì)劃在前后端傳輸用 RSA 加密,入庫(kù)用 AES 加密,足以。
到此這篇關(guān)于詳解Java中的敏感信息處理的文章就介紹到這了,更多相關(guān)Java敏感信息處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java調(diào)用Hbase報(bào)錯(cuò)解決方法
這篇文章主要為大家介紹了java調(diào)用Hbase報(bào)錯(cuò)解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-10-10Java實(shí)現(xiàn)數(shù)據(jù)更新和事件通知的觀察者模式
Java觀察者模式是一種行為型設(shè)計(jì)模式,用于實(shí)現(xiàn)對(duì)象間的一對(duì)多依賴關(guān)系。當(dāng)一個(gè)對(duì)象的狀態(tài)發(fā)生改變時(shí),它的所有依賴對(duì)象都會(huì)收到通知并自動(dòng)更新。觀察者模式可以實(shí)現(xiàn)松耦合,增強(qiáng)了系統(tǒng)的可維護(hù)性和可拓展性2023-04-04詳解Spring系列之@ComponentScan自動(dòng)掃描組件
這篇文章主要介紹了Spring @ComponentScan自動(dòng)掃描組件使用,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-06-06Linux環(huán)境卸載Centos7自帶的OpenJDK和安裝JDK1.8圖文教程
CentOS系統(tǒng)是開(kāi)發(fā)者常用的Linux操作系統(tǒng),安裝它時(shí)會(huì)默認(rèn)安裝自帶的舊版本的OpenJDK,但在開(kāi)發(fā)者平時(shí)開(kāi)發(fā)Java項(xiàng)目時(shí)還是需要完整的JDK,這篇文章主要給大家介紹了關(guān)于Linux環(huán)境卸載Centos7自帶的OpenJDK和安裝JDK1.8的相關(guān)資料,需要的朋友可以參考下2024-07-07SpringBoot2零基礎(chǔ)到精通之JUnit 5與指標(biāo)監(jiān)控
SpringBoot是一種整合Spring技術(shù)棧的方式(或者說(shuō)是框架),同時(shí)也是簡(jiǎn)化Spring的一種快速開(kāi)發(fā)的腳手架,本篇讓我們一起學(xué)習(xí)JUnit 5與指標(biāo)監(jiān)控2022-03-03