多國語言字符串的加密、全文檢索、模煳查詢的支持
標簽
PostgreSQL , 全文檢索 , 模煳查詢 , 前後模煳 , 多國字符串 , 透明加密 , 不可逆加密 , 可逆加密 , 函數安全 , C函數
背景
PostgreSQL中的全文檢索和模煳查詢是很好實現的。
通過pg_trgm插件,可以實現模煳查詢(前、後、全模煳),近似查詢,正則表達式查詢的索引加速。
通過中文分詞插件(pg_jieba, pg_scws, zhparser),可以實現中文分詞,其他語種的分詞,參考對應的插件。
但是,如果要讓數據庫支持加密存儲,同時對加密前的數據實現全文檢索和模煳查詢,有什麼方法呢?
數據加密
加密分為幾種,用戶應該根據實際情況來選擇。
可逆加密
可逆加密,例如pgcrypto插件,支持對稱加密等方法,用戶可以將數據存為加密後的形態。問題是對加密形態的數據,索引就不好弄了。
不過還好PostgreSQL支持表達式索引,可以對解密後的表達式創建索引,從而實現加速。
但是注意,索引的表達式就變加密前的了。
因此,這種方法,為了盡量的減少風險,也需要一些技巧來避免。
不可逆加密
不可逆加密,例如MD5,這個的索引就更不好弄了,但是是可以實現的,並且安全。
透明加密
透明加密,需要改造內核,數據存儲為加密狀態,在input和output時進行自動的加解密。
可逆加密數據的全文檢索和模煳查詢實現例子
1、創建crypto插件
create extension pgcrypto;
2、用超級用戶創建一個immutable秘鑰函數
create or replace function f1(int) returns text as $$
select md5(md5(md5($1::text)));
$$ language sql strict immutable;
如果用C函數,可以更好的隱藏。
也可以將秘鑰存在一個外部表(比如另一個數據庫、或者LDAP、或者其他網絡服務)中(通過PostgreSQL fdw接口可以訪問到即可),並賦予用戶相應的查詢權限才能得到。
3、屏蔽秘鑰函數的代碼
revoke select on pg_proc from public;
grant select(oid,proname,pronamespace,proowner,prolang,procost,prorows,provariadic,protransform,proisagg,proiswindow,prosecdef,proleakproof,proisstrict,proretset,provolatile,pronargs,pronargdefaults,prorettype,proargtypes,proallargtypes,proargmodes,proargnames,proargdefaults,probin,proconfig,proacl) on pg_proc to public;
revoke select(prosrc) on pg_proc from public;
revoke execute on function pg_get_functiondef(oid) from public;
4、設置秘鑰函數的權限
grant execute on function f1(int) to digoal;
5、創建測試表
create table test (id int , info bytea);
6、創建表達式(解密)索引
create or replace function enc(bytea,text,text) returns text as $$
select pgp_sym_decrypt($1,$2,$3);
$$ language sql strict immutable;
create index idx1 on test (enc(info, f1(1), 'cipher-algo=bf, compress-algo=2, compress-level=9'));
7、寫入舉例
insert into test values (1, pgp_sym_encrypt('abcdefg', f1(1), 'cipher-algo=bf, compress-algo=2, compress-level=9'));
8、查詢舉例
postgres=> select * from test;
id | info
----+------------------------------------------------------------------------------------------------------------------------------------------------------------
1 | \xc30d040403029b1c64cd9b1093ba62d23b019368155e5c6ff91bb144bc1c2852c9ab21971d62ea529056ff3a588229044ff54fe15292db6765c9d69ad0e6649f57b34f6e374883c87903b099
(1 row)
索引查詢
postgres=> select * from test where enc(info, f1(1), 'cipher-algo=bf, compress-algo=2, compress-level=9') = 'abcdefg';
id | info
----+------------------------------------------------------------------------------------------------------------------------------------------------------------
1 | \xc30d040403029b1c64cd9b1093ba62d23b019368155e5c6ff91bb144bc1c2852c9ab21971d62ea529056ff3a588229044ff54fe15292db6765c9d69ad0e6649f57b34f6e374883c87903b099
(1 row)
postgres=> explain select * from test where enc(info, f1(1), 'cipher-algo=bf, compress-algo=2, compress-level=9') = 'abcdefg';
QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------------------------------------------------
Bitmap Heap Scan on test (cost=1.40..6.78 rows=6 width=36)
Recheck Cond: (pgp_sym_decrypt(info, '40f5888b67c748df7efba008e7c2f9d2'::text, 'cipher-algo=bf, compress-algo=2, compress-level=9'::text) = 'abcdefg'::text)
-> Bitmap Index Scan on idx1 (cost=0.00..1.40 rows=6 width=0)
Index Cond: (pgp_sym_decrypt(info, '40f5888b67c748df7efba008e7c2f9d2'::text, 'cipher-algo=bf, compress-algo=2, compress-level=9'::text) = 'abcdefg'::text)
(4 rows)
9、支持全文檢索、模煳查詢
針對表達式 enc(info, f1(1), 'cipher-algo=bf, compress-algo=2, compress-level=9')
創建對應的索引,即可實現全文檢索和模煳查詢。
不可逆加密數據的全文檢索和模煳查詢實現例子
不可逆加密碼實現全文檢索和模煳查詢,我們需要對字符串TOKEN化,然後對TOKEN進行不可逆加密,存成數組。
查詢時,使用查詢串進行TOKEN話,對TOKEN進行不可逆加密,然後進行數組的包含或相交的索引檢索,加速查詢。
同時實現最高安全。
例子表
create table tbl(
id int,
info bytea, -- 原始字符串加密存儲,使用可逆加密(秘鑰存儲在客戶端),不建立索引。
info_arr text[] -- 客戶端解密後,TOKEN化,然後使用不可逆加密,得到加密後的TOKEN數組。
);
1、寫入
1、內容串TOKEN化(全文檢索)
使用對應語言的全文檢索插件,對字符串進行全文檢索TOKEN化。
得到單字以及詞組的TOKEN(tsvector)。然後將tsvector轉換為array。
tsvector_to_array(tsvector) 得到 text[]
2、內容串TOKEN化(雙字TOKEN)
create or replace function two_token(text) returns text[] as $$
declare
res text[] := '{}'::text[];
len int := length($1);
begin
if len<=1 then
return array[$1];
end if;
for i in 1..len-1 loop
res := array_append(res, substring($1, i, 2));
end loop;
return res;
end;
$$ language plpgsql strict immutable;
postgres=> select two_token('abcde');
two_token
---------------
{ab,bc,cd,de}
(1 row)
3、內容串TOKEN化(單字TOKEN)
使用regexp_split_to_array得到單字數組
postgres=# select regexp_split_to_array('abcde','');
regexp_split_to_array
-----------------------
{a,b,c,d,e}
(1 row)
4、加密TOKEN
create or replace function md5_token(text[]) returns text[] as $$
declare
res text[] := '{}'::text[];
i text;
begin
foreach i in array $1 loop
res := array_append(res, md5(i));
end loop;
return res;
end;
$$ language plpgsql strict immutable;
postgres=> select md5_token(two_token('abcde'));
md5_token
---------------------------------------------------------------------------------------------------------------------------------------
{187ef4436122d1cc2f40dc2b92f0eba0,5360af35bde9ebd8f01f492dc059593c,6865aeb3a9ed28f9a79ec454b259e5d0,5f02f0889301fd7be1ac972c11bf3e7d}
(1 row)
5、存儲加密後的數組
insert into tbl values (1, 客戶端加密的bytea, 加密後的TEXT數組);
6、數組GIN索引
create index idx on tbl using gin (info_arr);
2、查詢
1、查詢串TOKEN化,加密
postgres=> select md5_token(two_token('abcde'));
md5_token
---------------------------------------------------------------------------------------------------------------------------------------
{187ef4436122d1cc2f40dc2b92f0eba0,5360af35bde9ebd8f01f492dc059593c,6865aeb3a9ed28f9a79ec454b259e5d0,5f02f0889301fd7be1ac972c11bf3e7d}
(1 row)
2、一級查詢過濾
select * from tbl where info_arr @> md5_token(two_token('abcde'));
或
select * from tbl where info_arr && md5_token(two_token('abcde'));
3、二級CPU過濾
一級過濾使用了GIN索引,二級過濾使用CPU運算。
做到了高效和安全兼具。
select * from tbl where info_arr && md5_token(two_token('abcde')) and 對稱加密解密(info,'秘鑰') ~ '正則表達式';
小結
1、透明加密當然是最好的,但是實現需要改造PG內核。
拖庫:安全。
數據庫被攻擊:安全。
2、不可逆加密,安全性很高,但是會指數級的放大存儲空間。
拖庫:安全。
數據庫被攻擊:安全。
3、可逆加密,安全性一般,前提是使用安全的秘鑰函數(不要使用明文秘鑰),即使這樣,如果數據庫用戶被攻,用戶還是能將明文拖走。
拖庫:不安全。
數據庫被攻擊:有限安全。(超級用戶、OWNER權限被獲取時,不安全。)
參考
1、透明加密
《PostgreSQL 透明加密(TDE,FDE) - 塊級加密》
2、pg_trgm
https://www.postgresql.org/docs/10/static/pgtrgm.html
《中文模煳查詢性能優化 by PostgreSQL trgm》
3、pg_scws
https://github.com/jaiminpan/pg_scws
4、pg_jieba
https://github.com/jaiminpan/pg_jieba
5、zhparser
https://github.com/amutu/zhparser
6、《PostgreSQL 函數代碼隱藏 - How to control who can see PostgreSQL function's source code》
7、PostgreSQL C函數例子:
https://www.postgresql.org/docs/10/static/xfunc-c.html
8、《固若金湯 - PostgreSQL pgcrypto加密插件》
最後更新:2017-10-29 00:03:53