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