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