字節(jié)終面:數(shù)據(jù)庫加密后怎么做模糊查詢?
代碼地址: https://github.com/zuiyu-main/EncryptDemo
在個別特殊領(lǐng)域中,數(shù)據(jù)的安全問題是非常的重要的,所以需要數(shù)據(jù)庫存儲的數(shù)據(jù)是需要加密存儲的。所以也就引申出來本文這個問題,加密之后的密文,還能模糊檢索嗎,如果能檢查,如何做模糊檢索呢?
現(xiàn)在的系統(tǒng)設(shè)計中,常見的加密字段有、密碼、身份證號、手機號、住址信息、銀行卡、信用卡以及個別行業(yè)的敏感信息。這些信息對加密的要求也不一樣,對于密碼來說,一般使用不可逆的加密算法就可以,一般不會用到檢索。但是對于身份證號或者個別領(lǐng)域中的中文信息,我們是需要支持密文模糊匹配的,下面我們就來看看有哪些實現(xiàn)方式。
本來主要講兩種常規(guī)的簡單加密做法,主要目標(biāo)為能實現(xiàn)密文的模糊查詢。下面來跟我看第一種。
常規(guī)分詞加密
常規(guī)加密的密文檢索功能根據(jù)4位英文字符(半角),2個中文字符(全角)作為一個檢索條件,將一個字段拆分為多個字段。
比如:zuiyu123
使用4個字符為一組的加密方式。
第一組 zuiy,第二組uiyu,第三組iyu1,第四組yu12,第五組u123...如果字符串很長,依次類推下去。
如果需要檢索所有包含檢索條件 uiyu 的數(shù)據(jù),加密字符后通過 key like ‘%加密uiyu的密文%’查詢。
所以這種實現(xiàn)方式就會有一種問題就是,隨著加密字符串的增加,密文也會變的越大,所以一般用此處方式需要注意數(shù)據(jù)庫中的字段長度限制。
需要注意的是,使用此處方式有一定的限制:
- 支持模糊檢索加密,但是加密的密文隨原文長度增長。
- 支持的模糊檢索條件必須大于等于4個英文數(shù)字或者2個漢字,不支持短的查詢(自定義該局限性,業(yè)界常用的就是4個英文數(shù)字或者2個漢字,再短的長度不建議支持,因為分詞組合會增多從而導(dǎo)致存儲的成本增加,反而安全性降低。)。
- 返回的列表不是很精確,需要二次篩選,先解密在進(jìn)一步篩選。
字符串拆分的代碼如下:
protected List<String> loopEncryptString(String input, int chunkSize) {
int length = input.length();
List<String> strList = new LinkedList<>();
for (int i = 0; i < length; i++) {
StringBuilder chunkBuilder = new StringBuilder();
for (int j = 0; j < chunkSize; j++) {
int index = (i + j) % length;
chunkBuilder.append(input.charAt(index));
}
strList.add(chunkBuilder.toString());
log.info("第 {} 組:[{}]",i+1,chunkBuilder);
// 如果到了最后一個分組,則不再循環(huán)第一個字符
if (i + chunkSize >= length) {
break;
}
}
log.info("分詞結(jié)果:[{}]",strList);
return strList;
}
對于上述文本zuiyu123分詞效果如下
下面來看下中文的分詞效果:
檢索一下,只要我們使用的是包含上述分詞結(jié)果的條件我們就可以檢索的到。
比如我們檢索個蛋白質(zhì):
search result:[[{ID=8dac4d97-f05f-472e-94b2-02828aa235d6, CONTENT=ELYJBkZbfiVaJgTdlgglDg==UYwxxmEMQ9hq1jOax+r5rg==WwCBtglEf6clcWajP9sK+A==4sEGCqZ4P8Osr0dW84zFEA==c2AZejHeUp/5gpPkexfNcg==pvh/TcZRO4zwD+kwbE9lHw==1g30dxyz7z+8TQq+8jYH1A==AsWZOeiprypfrzSK3FtOuw==01vpoSuCXOpKCgcPsNlXyQ==79BPmIhSwMaA7hjN3ENDxA==}]]
可以看到,上述的content字段的內(nèi)容長度非常的長,所以我們要注意數(shù)據(jù)庫字段長度限制。
除了上面這個方式外,發(fā)散一下思維,如果你用過 Elasticsearch 的話,會不會有點想法呢?
因為在中文的場景中,中文既然要分詞,選擇專業(yè)的分詞器應(yīng)該是更合理的啊,所以我們可以使用???
對的,你沒猜錯,既然是要分詞,對于特殊的中文業(yè)務(wù)場景,直接使用 Elasticsearch 的分詞器分詞不就好了嗎,然后再用 Elasticsearch 的強大檢索能力,不就可以滿足我們的模糊檢索需求了嗎,想到就去做,下面就跟著我一起來看下如果用 Elasticsearch 的分詞實現(xiàn)密文模糊檢索。
分詞器分詞檢索
使用分詞器分詞進(jìn)行密文檢索的原理:
- 使用 Elasticsearch 自帶的正則分詞器對加密后的密文進(jìn)行分詞。
- 檢索時使用 Elasticsearch 的match進(jìn)行檢索。
本文演示使用AES進(jìn)行加解密,所以分詞器我就直接使用正則匹配,將密文中的內(nèi)容按照==進(jìn)行拆分。
下面我們一起進(jìn)入代碼時間,跟隨著我的腳本來看看分詞密文檢索是什么樣的。
創(chuàng)建一個使用pattern分詞器的索引encrypt
如下創(chuàng)建索引語句為 Elasticsearch 6.8 的語句,如果使用 7+、8+ 的需要修改為對應(yīng)的版本。
mappings 中的 _doc
put 127.0.0.1:9200/encrypt
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "my_tokenizer"
}
},
"tokenizer": {
"my_tokenizer": {
"type": "pattern",
"pattern": "=="
}
}
}
},
"mappings": {
"_doc": {
"properties": {
"content": {
"type": "text"
}
}
}
}
}
隨便對于一個密文進(jìn)行分詞,可以看到,已經(jīng)按照我們的語氣進(jìn)行==拆分為多個詞語了
其實不難發(fā)現(xiàn),我們使用 AES 加密,就是對分詞之后的每個詞語進(jìn)行加密,然后組成一個新的字符串。
還是上面那句話魚肉的蛋白質(zhì)含量真的高,我們看一下分詞結(jié)果。
所以我們按照==拆分之后,檢索式再通過加密之后的密文進(jìn)行檢索,也就相當(dāng)于分詞檢索了。
檢索結(jié)果如下:
search result:[{"hits":[{"_index":"encrypt","_type":"_doc","_source":{"content":"ELYJBkZbfiVaJgTdlgglDg==9hF4g5NErtZNS9qFJGYeZA==uH9W7jvdoLIKq5gOpFjhWg==4sEGCqZ4P8Osr0dW84zFEA==c2AZejHeUp/5gpPkexfNcg==1g30dxyz7z+8TQq+8jYH1A==01vpoSuCXOpKCgcPsNlXyQ==kIzJL/y/pnUbkZGjIkz4tw=="},"_id":"1713343285459","_score":2.8951092}],"total":1,"max_score":2.8951092}]
總結(jié)
密文的模糊查詢就是以空間成本換取的。相比于存儲原文,密文比原文增長了好幾倍。
所以根據(jù)你的業(yè)務(wù)場景,選擇一個合適的加密算法才是最優(yōu)解。