加密的手機號,如何模糊查詢?

蘇三說技術發表於2023-10-12

前言

前幾天,知識星球中有位小夥伴,問了我一個問題:加密的手機號如何模糊查詢?

我們都知道,在做系統設計時,考慮到系統的安全性,需要對使用者的一些個人隱私資訊,比如:登入密碼、身份證號、銀行卡號、手機號等,做加密處理,防止使用者的個人資訊被洩露。

很早之前,CSDN遭遇了SQL隱碼攻擊,導致了600多萬條明文儲存的使用者資訊被洩。

因此,我們在做系統設計的時候,要考慮要把使用者的隱私資訊加密儲存。

常見的對稱加密演算法有 AES、SM4、ChaCha20、3DES、DES、Blowfish、IDEA、RC5、RC6、Camellia等。

目前國際主流的對稱加密演算法是AES,國內主推的則是SM4

無論是用哪種演算法,加密前的字串,和加密後的字串,差別還是比較大的。

比如加密前的字串:蘇三說技術,使用金鑰:123,生成加密後的字串為:U2FsdGVkX1+q7g9npbydGL1HXzaZZ6uYYtXyug83jHA=

如何對加密後的字串做模糊查詢呢?

比如:假設查詢蘇三關鍵字,加密後的字串是:U2FsdGVkX19eCv+xt2WkQb5auYo0ckyw

上面生成的兩個加密字串差異看起來比較大,根本沒辦法直接透過SQL語句中的like關鍵字模糊查詢。

那我們該怎麼實現加密的手機號的模糊查詢功能呢?

1 一次載入到記憶體

實現這個功能,我們第一個想到的辦法可能是:把個人隱私資料一次性載入到記憶體中快取起來,然後在記憶體中先解密,然後在程式碼中實現模糊搜尋的功能。

圖片這樣做的好處是:實現起來比較簡單,成本非常低。

但帶來的問題是:如果個人隱私資料非常多的話,應用伺服器的記憶體不一定夠用,可能會出現OOM問題。

還有另外一個問題是:資料一致性問題。

如果使用者修改了手機號,資料庫更新成功了,需要同步更新記憶體中的快取,否則使用者查詢的結果可能會跟實際情況不一致。

比如:資料庫更新成功了,記憶體中的快取更新失敗了。

或者你的應用,部署了多個伺服器節點,有一部分記憶體快取更新成功了,另外一部分剛好在重啟,導致更新失敗了。

該方案不僅可能會導致應用伺服器出現OOM問題,也可能會導致系統的複雜度提升許多,總體來說,有點得不償失。

2 使用資料庫函式

既然資料庫中儲存的是加密後的字串,還有一種方案是使用資料庫的函式解密。

我們可以使用MySQL的DES_ENCRYPT函式加密,使用DES_DECRYPT函式解密:

SELECT 
DES_DECRYPT('U2FsdGVkX1+q7g9npbydGL1HXzaZZ6uYYtXyug83jHA=', '123'); 

應用系統重所有的使用者隱私資訊的加解密都在MySQL層實現,不存在加解密不一致的情況。

該方案中儲存資料時,只對單個使用者的資料進行操作,資料量比較小,效能還好。

但模糊查詢資料時,每一次都需要透過DES_DECRYPT函式,把資料庫中使用者某個隱私資訊欄位的所有資料都解密了,然後再透過解密後的資料,做模糊查詢。

如果該欄位的資料量非常大,這樣每次查詢的效能會非常差。

3 分段儲存

我們可以將一個完整的字串,拆分成多個小的字串。

以手機號為例:18200256007,按每3位為一組,進行拆分,拆分後的字串為:182,820,200,002,025,256,560,600,007,這9組資料。

然後建一張表:

CREATE TABLE `encrypt_value_mapping` (
  `id` bigint NOT NULL COMMENT '系統編號',
  `ref_id` bigint NOT NULL COMMENT '關聯絡統編號',
  `encrypt_value` varchar(255) NOT NULL COMMENT '加密後的字串'
) ENGINE=InnoDB  CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='分段加密對映表'

這張表有三個欄位:

  • id:系統編號。
  • ref_id:主業務表的系統編號,比如使用者表的系統編號。
  • encrypt_value:拆分後的加密字串。

使用者在寫入手機號的時候,同步把拆分之後的手機號分組資料,也一起寫入,可以保證在同一個事務當中,保證資料的一致性。

如果要模糊查詢手機號,可以直接透過encrypt_value_mapping的encrypt_value模糊查詢出使用者表的ref_id,再透過ref_id查詢使用者資訊。

具體sql如下:

select s2.id,s2.name,s2.phone 
from encrypt_value_mapping s1
inner join `user` s2 on s1.ref_id=s2.id
where s1.encrypt_value = 'U2FsdGVkX19Se8cEpSLVGTkLw/yiNhcB'
limit 0,20;

這樣就能輕鬆的透過模糊查詢,搜尋出我們想要的手機號了。

注意這裡的encrypt_value用的等於號,由於是等值查詢,效率比較高。

注意:這裡透過sql語句查詢出來的手機號是加密的,在介面返回給前端之前,需要在程式碼中統一做解密處理。

為了安全性,還可以將加密後的明文密碼,用*號增加一些干擾項,防止手機號被洩露,最後展示給使用者的內容,可以顯示成這樣的:182***07

4 其他的模糊查詢

如果除了使用者手機號,還有其他的使用者隱私欄位需要模糊查詢的場景,該怎麼辦?

我們可以將encrypt_value_mapping表擴充套件一下,增加一個type欄位。

該欄位表示資料的型別,比如:1.手機號 2.身份證 3.銀行卡號等。

這樣如果有身份證和銀行卡號模組查詢的業務場景,我們可以透過type欄位做區分,也可以使用這套方案,將資料寫入到encrypt_value_mapping表,最後根據不同的type查詢出不同的分組資料。

如果業務表中的資料量少,這套方案是可以滿足需求的。

但如果業務表中的資料量很大,一個手機號就需要儲存9條資料,一個身份證或者銀行卡號也需要儲存很多條資料,這樣會導致encrypt_value_mapping表的資料急劇增加,可能會導致這張表非常大。

最後的後果是非常影響查詢效能。

那麼,這種情況該怎麼辦呢?

5 增加模糊查詢欄位

如果資料量多的情況下,將所有使用者隱私資訊欄位,分組之後,都集中到一張表中,確實非常影響查詢的效能。

那麼,該如何最佳化呢?

答:我們可以增加模糊查詢欄位。

還是以手機模糊查詢為例。

我們可以在使用者表中,在手機號旁邊,增加一個encrypt_phone欄位。

CREATE TABLE `user` (
  `id` int NOT NULL,
  `code` varchar(20)  NOT NULL,
  `age` int NOT NULL DEFAULT '0',
  `name` varchar(30) NOT NULL,
  `height` int NOT NULL DEFAULT '0',
  `address` varchar(30)  DEFAULT NULL,
  `phone` varchar(11) DEFAULT NULL,
  `encrypt_phone` varchar(255)  DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='使用者表'

然後我們在儲存資料的時候,將分組之後的資料拼接起來。

還是以手機號為例:

18200256007,按每3位為一組,進行拆分,拆分後的字串為:182,820,200,002,025,256,560,600,007,這9組資料。

分組之後,加密之後,用逗號分割之後拼接成這樣的資料:,U2FsdGVkX19Se8cEpSLVGTkLw/yiNhcB,U2FsdGVkX1+qysCDyVMm/aYXMRpCEmBD,U2FsdGVkX19oXuv8m4ZAjz+AGhfXlsQk,U2FsdGVkX19VFs60R26BLFzv5nDZX40U,U2FsdGVkX19XPO0by9pVw4GKnGI3Z5Zs,U2FsdGVkX1/FIIaYpHlIlrngIYEnuwlM,U2FsdGVkX19s6WTtqngdAM9sgo5xKvld,U2FsdGVkX19PmLyjtuOpsMYKe2pmf+XW,U2FsdGVkX1+cJ/qussMgdPQq3WGdp16Q。

以後可以直接透過sql模糊查詢欄位encrypt_phone了:

select id,name,phone
from user where encrypt_phone like '%U2FsdGVkX19Se8cEpSLVGTkLw/yiNhcB%'
limit 0,20;

注意這裡的encrypt_value用的like

這裡為什麼要用逗號分割呢?

答:是為了防止直接字串拼接,在極端情況下,兩個分組的資料,原本都不滿足模糊搜尋條件,但拼接在一起,卻有一部分滿足條件的情況發生。

當然你也可以根據實際情況,將逗號改成其他的特殊字元。

此外,其他的使用者隱私欄位,如果要實現模糊查詢功能,也可以使用類似的方案。

最後說一句,雖說本文介紹了多種加密手機號實現模糊查詢功能的方案,但我們要根據實際業務場景來選擇,沒有最好的方案,只有最合適的。

 

最後歡迎大家加入蘇三的知識星球【Java突擊隊】,一起學習。

星球中有很多獨家的乾貨內容,比如:Java後端學習路線,分享實戰專案,原始碼分析,百萬級系統設計,系統上線的一些坑,MQ專題,真實面試題,每天都會回答大家提出的問題,免費修改簡歷,免費回答工作中的問題。

星球目前開通了9個優質專欄:技術選型、系統設計、踩坑分享、工作實戰、底層原理、Spring原始碼解讀、痛點問題、高頻面試題 和 效能最佳化。

 

 

加入星球如果不滿意,3天內包退。

 

相關文章