跳转至

数据脱敏

数据脱敏

功能说明

SeaboxSQL内置安全策略(Security Policy)模型,可通过配置一系列安全策略来对用户行为进行识别和保护,提供了包括保护用户敏感数据的能力。

动态数据脱敏策略(Dynamic Data Masking)是Security Policy模型支持的一类安全策略,数据控制者对用户表中的敏感数据识别后(敏感数据发现和识别不在该特性范围内),对包含敏感列的资源标签配置数据脱敏策略,并依据不同的应用场景来限制用户对数据的访问行为和信息提取行为,以达到对敏感信息保护的能力。常用在生产环境,在访问敏感数据即时进行脱敏,一般用来解决在生产环境需要根据不同情况对同一敏感数据读取时进行不同级别脱敏的场景。动态数据脱敏并不会真正改动表中存储的实际数据,只是在查询的时候应用该特性控制查询返回的数据。

动态数据脱敏(DDM)通过创建脱敏策略、内置脱敏函数、UNMASK权限控制实现。

SeaboxSQL支持的动态数据脱敏功能如下图架构

动态数据脱敏示意图

  1. 配置脱敏策略

安全管理员或系统管理员通过在指定表和字段上创建脱敏策略(MASKING POLICY ),设置脱敏方式,为表的字段设置安全属性。

其中脱敏方式支持内置脱敏函数、其他内置函数、通用SQL表达式、自定义函数(支持C/Python/JAVA等语言开发的UDF)

  1. 设置用户权限

安全管理员或系统管理员可将unmask权限赋予不同的用户或角色。不具备该权限的用户在执行包含上述设置了脱敏策略的字段的查询语句时,SQL解析器将查询字段改写为策略指定的表达式;而具备该权限的用户不会改写。

脱敏策略的配置主要包括三个方面——脱敏方式(Masking Function)、脱敏对象(Resource Label)、用户过滤器(Masking Filter)。

脱敏策略管理

创建脱敏策略
语法
sql CREATE MASKING POLICY policy_name ON table_name(column_name) USING EXPRESSION expression_stmt;
说明

为指定表的字段创建脱敏策略。其中关键字含义如下:

policy_name
策略名称,标识脱敏策略的名称
table_name
指定表名
column_name
指定字段名
expression_stmt
指定脱敏表达式,可为通用的SQL表达式或函数,例如a+1random()*10000case b=1 then 10 else 100 end 等;SeaboxSQL也提供专门为脱敏增加的函数,包括随机脱敏函数masking_random()和自定义脱敏函数masking_partial();此外脱敏表达式支持SQL、C、Python、Java等SeaboxSQL支持的多种语言定义的UDF。
示例

以下SQL标识为表t1中a字段创建名为p1的脱敏策略,该策略使用内置脱敏函数masking_random(1,100)

 seaboxsql=> CREATE MASKING POLICY p1 ON t1(a) USING EXPRESSION masking_random(1,100);
 CREATE MASKING POLICY
注意
  1. 创建时指定的脱敏策略名必须唯一。若指定的脱敏策略名(假设为p1)已经存在,则报错如下:
seaboxsql=> CREATE MASKING POLICY p1 ON t1(a) USING EXPRESSION a+5;
ERROR:  masking policy "p1" for table "t1" already exists
  1. 创建时指定的脱敏字段上不能有脱敏策略。若指定的脱敏字段上已存在脱敏策略(假设t1表的a字段上已有脱敏策略),则报错如下:
seaboxsql=> CREATE MASKING POLICY p2 ON t1(a) USING EXPRESSION a+5;
ERROR:  duplicate key value violates unique constraint "sd_masking_policy_polrelid_polattr_index"
DETAIL:  Key (polrelid, polattr)=(16386, 1) already exists.
  1. 创建脱敏策略时会对脱敏表达式做类型检查。若脱敏表达式的结果类型无法转换为脱敏字段的类型,则报错。例如:脱敏字段为整型,脱敏表达式结果为text型,报错信息如下:
seaboxsql=> CREATE MASKING POLICY p1 ON t1(a) USING EXPRESSION 'fdfdsd'::text;
ERROR:  column "a" is of type integer but default expression is of type text
HINT:  You will need to rewrite the expression.
修改脱敏策略
语法
sql ALTER MASKING POLICY policy_name ON table_name(column_name) USING EXPRESSION expression_stmt;
说明

为指定表的字段修改脱敏策略。其中关键字含义如下:

policy_name
策略名称,标识脱敏策略的名称
table_name
指定表名
column_name
指定字段名
expression_stmt
指定脱敏表达式,可为通用的SQL表达式或函数,例如a+1random()*10000case b=1 then 10 else 100 end 等;SeaboxSQL也提供专门为脱敏增加的函数,包括随机脱敏函数masking_random()和自定义脱敏函数masking_partial();此外脱敏表达式支持SQL、C、Python、Java等SeaboxSQL支持的多种语言定义的UDF。
示例

以下SQL标识为表t1中a字段修改名为p1的脱敏策略,该策略改为使用内置脱敏函数masking_random(2,200)

 seaboxsql=> ALTER MASKING POLICY p1 ON t1(a) USING EXPRESSION masking_random(2,200);
 ALTER MASKING POLICY
注意
  1. 修改脱敏策略时指定的策略名和脱敏字段必须匹配。例如,策略p1创建在a字段上,修改时指定策略名为p1,字段为b,则报错如下:
seaboxsql=> ALTER MASKING POLICY p1 ON t1(b) USING EXPRESSION masking_random(2,200);
ERROR:  column "b" doesn't match the policy "p1" in the table
  1. 修改脱敏策略时同样会对脱敏表达式做类型检查。若脱敏表达式的结果类型无法转换为脱敏字段的类型,则报错。例如:脱敏字段为整型,脱敏表达式结果为text,报错信息如下:
seaboxsql=> ALTER MASKING POLICY p1 ON t1(a) USING EXPRESSION 'fdfdsd'::text;
ERROR:  column "a" is of type integer but default expression is of type text
HINT:  You will need to rewrite the expression.
删除脱敏策略
语法
sql DROP MASKING POLICY policy_name ON table_name;
说明

为指定表删除脱敏策略。其中关键字含义如下:

policy_name
策略名称,标识脱敏策略的名称
table_name
指定表名
示例

以下SQL标识为表t1删除名为p1的脱敏策略

 seaboxsql=> DROP MASKING POLICY p1 ON t1;
 DROP MASKING POLICY
注意
删除时指定的脱敏策略名必须存在于指定的表上。若指定的脱敏策略名(假设为p2)不存在,则报错如下:
seaboxsql=> DROP MASKING POLICY p2 ON t1;
ERROR:  masking policy "p2" for table "t1" does not exist

用户权限控制

将unmask权限赋予用户或角色
语法
sql GRANT UNMASK ON table_name TO user_name;
说明
授予用户unmask权限,即可绕过脱敏策略查看到真实数据。
示例

以下示例为u1用户赋予t1和t3表的unmask权限,使用u1用户可查看字段的实际存储内容。

seaboxsql=> reset role;
RESET
seaboxsql=# GRANT unmask ON t1,t3 to u1;
GRANT
seaboxsql=# set role u1;
SET
seaboxsql=> select * from t1;
 a  
----
 20
 30
 10
(3 rows)

seaboxsql=> select * from t3;
  names   |    phone    |      id_card       
----------+-------------+--------------------
 王小春   | 13934629845 | 300400193612215635
 张三     | 13972314443 | 100200198809083214
 欧阳正华 | 18746563244 | 500600201603315338
(3 rows)
将unmask权限回收
语法
sql REVOKE UNMASK ON table_name from user_name;
说明
回收用户在表上的unmask权限,回收后,用户查询表上具有脱敏策略的字段时就会脱敏。
示例

以下示例回收u1用户在t1和t3表上的unmask权限,回收后,u1用户查询t1、t3表上的脱敏字段时脱敏显示。

seaboxsql=> reset role;
RESET
seaboxsql=# REVOKE unmask ON t1,t3 from u1;
REVOKE
seaboxsql=# set role u1;
SET
seaboxsql=> select * from t1;
a  
----
91
56
31
(3 rows)
seaboxsql=> select * from t3;
names |    phone    |      id_card       
-------+-------------+--------------------
张**  | 139xxxx4443 | 100200xxxxxxxx3214
王**  | 139xxxx9845 | 300400xxxxxxxx5635
欧**  | 187xxxx3244 | 500600xxxxxxxx5338
(3 rows)

内置脱敏函数

SeaboxSQL也提供专门为脱敏增加的函数,包括随机脱敏函数masking_random()和自定义脱敏函数masking_partial(),其他常见脱敏策略可通过SQL表达式实现。

内置脱敏函数是内置函数的一部分,也可用于普通查询语句中,并非仅在脱敏策略中使用。

masking_random(start, end)
语法
masking_random(start, end)
说明
随机脱敏函数,该函数只对数字类型起作用,产生一介于start和end之间的随机值。若start和end均为整型,则产生一整型值;若start和end存在数值型,则产生一数值型的值。
示例

以下示例说明该函数产生整型值和数值型值的方式。

seaboxsql=# SELECT masking_random(1,100) from generate_series(1,3);
masking_random 
----------------
            21
            97
            55
(3 rows)
seaboxsql=# SELECT masking_random(1.0,100.0) from generate_series(1,3);
masking_random    
---------------------
90.1585956726086050
68.4785984092504570
44.4470343846860530
(3 rows)
注意

start必须小于end,否则报错如下:

seaboxsql=# select masking_random(6,3);
ERROR:  start value=6 must be less than end value=3
masking_partial()
语法
masking_partial(string, prefix, padding, suffix)
说明

自定义脱敏函数,对字符列进行脱敏,返回一经过遮挡的字符串,用户可以设定以下4个参数:

string
要被脱敏的字符串
prefix
前端要保留显示的字符数
padding
用来遮挡的字符串
suffix
后端要保留显示的字符数。

如果prefix+suffix长度大于string实际字符数量,则直接显示padding的字符内容。

示例

以下示例说明实现手机号脱敏,保留手机号码13994387652前3位和后4位,中间部分用xxxx替换,最终结果为'139xxxx7652':

 seaboxsql=> select masking_partial('13994387652', 3, 'xxxx', 4);
  masking_partial       
 -----------------
 139xxxx7652
 (3 rows)
同上述示例,还可利用自定义脱敏实现信用卡脱敏邮件脱敏全数字脱敏全脱敏等。

信用卡脱敏

比如,保留前6位和后4位,出生年月日被脱敏为'**'

seaboxsql=# select masking_partial('500600201603315338', 6, '********', 4);
masking_partial   
--------------------
500600********5338
(1 row)
邮件脱敏

比如,前4位被脱敏为'XX',保留其它位

seaboxsql=# select a from t1;
        a           
-----------------------
wangjianguo@12345.com
(1 row)

seaboxsql=# select masking_partial(a, 0, 'XX', length(a)-4) from t1;
masking_partial   
---------------------
XXjianguo@12345.com
(1 row)
全数字脱敏

比如,脱敏为'12345'

seaboxsql=# select a from t1;
    a     
----------
我爱祖国
(1 row)
seaboxsql=# select masking_partial(a, 0, '12345', 0) from t1;
masking_partial 
-----------------
12345
(1 row)
全脱敏

比如,脱敏为'MASKALL'

seaboxsql=# select a from t1;
    a     
----------
我爱祖国
(1 row)
seaboxsql=# select masking_partial(a, 0, 'MASKALL', 0) from t1;
masking_partial 
-----------------
MASKALL
(1 row)

数据脱敏示例

使用内置函数

以下示例中为表t1的字段a设置了使用masking_random()内置脱敏函数的策略,为t3表的names、phone、id_card字段分别设置了使用masking_partial()内置脱敏函数的策略。

seaboxsql=# create role u1;
CREATE ROLE
seaboxsql=# create role u2;
CREATE ROLE
seaboxsql=# create table t1 (a int);
CREATE TABLE
seaboxsql=# insert into t1 values(10),(20),(30);
INSERT 0 3
seaboxsql=# create table t3(names text, phone text, id_card text);
CREATE TABLE
seaboxsql=# insert into t3 values('张三','13972314443', '100200198809083214');
INSERT 0 1
seaboxsql=# insert into t3 values('王小春','13934629845', '300400193612215635');
INSERT 0 1
seaboxsql=# insert into t3 values('欧阳正华','18746563244', '500600201603315338');
INSERT 0 1
seaboxsql=# ALTER TABLE t1 owner to sd_sso;
ALTER TABLE
seaboxsql=# ALTER TABLE t3 owner to sd_sso;
ALTER TABLE
seaboxsql=# set role sd_sso;
SET
seaboxsql=> CREATE MASKING POLICY p1 ON t1(a) USING EXPRESSION masking_random(1,100);
CREATE MASKING POLICY
seaboxsql=> CREATE MASKING POLICY p4 on t3(names) USING EXPRESSION masking_partial(names, 1, '**', 0);
CREATE MASKING POLICY
seaboxsql=> CREATE MASKING POLICY p5 on t3(phone) USING EXPRESSION masking_partial(phone, 3, 'xxxx', 4);
CREATE MASKING POLICY
seaboxsql=> CREATE MASKING POLICY p6 on t3(id_card) USING EXPRESSION masking_partial(id_card, 6, 'xxxxxxxx', 4);
CREATE MASKING POLICY
seaboxsql=> 
seaboxsql=> GRANT SELECT ON t1,t3 to u1;
GRANT
seaboxsql=> 
seaboxsql=> set role u1;
SET
seaboxsql=> select * from t1;
 a  
----
 91
 56
 31
(3 rows)

seaboxsql=> select * from t3;
 names |    phone    |      id_card       
-------+-------------+--------------------
 **  | 139xxxx4443 | 100200xxxxxxxx3214
 **  | 139xxxx9845 | 300400xxxxxxxx5635
 **  | 187xxxx3244 | 500600xxxxxxxx5338
(3 rows)
使用自定义表达式

以下是脱敏策略使用自定义表达式来替换原字段值的例子

seaboxsql=# set role sd_sso;
SET
seaboxsql=> ALTER MASKING POLICY p1 ON t1(a) USING EXPRESSION a*2;
ALTER MASKING POLICY
seaboxsql=> 
seaboxsql=> set role u1;
SET
seaboxsql=> select * from t1;
 a  
----
 20
 40
 60
(3 rows)
使用UDF

也可以使用用户自定义函数(UDF)来实现用户更靠近业务的定制化需求,支持SQL、C/C++、Python、Java语言定义的UDF。以下是使用C语言UDF的示例.

  1. 首先,创建agg.c文件并得到共享库agg.so
#include "postgres.h"
#include "fmgr.h"

PG_MODULE_MAGIC;

PG_FUNCTION_INFO_V1(add);

Datum
agg(PG_FUNCTION_ARGS)
{
    int32    arg = PG_GETARG_INT32(0);

    PG_RETURN_INT32(arg + 5);
}
  1. 然后,使用自定义函数agg5进行脱敏:
seaboxsql=> reset role;
RESET
seaboxsql=# drop table if exists t2;
NOTICE:  table "t2" does not exist, skipping
DROP TABLE
seaboxsql=# create table t2 as select * from t1;
SELECT 3
seaboxsql=# GRANT SELECT ON t2 to u1;
GRANT
seaboxsql=# 
seaboxsql=# CREATE FUNCTION agg5(int)
seaboxsql-#   RETURNS int AS '/home/sy/seaboxsql/mpp/agg.so', 'agg'
seaboxsql-#   LANGUAGE C STRICT;
CREATE FUNCTION
seaboxsql=# ALTER TABLE t2 owner to sd_sso;
ALTER TABLE
seaboxsql=# set role sd_sso;
SET
seaboxsql=> CREATE MASKING POLICY p2 ON t2(a) USING EXPRESSION agg5(a);
CREATE MASKING POLICY
seaboxsql=# 
seaboxsql=> set role u1;
SET
seaboxsql=> select * from t2;
 a  
----
 15
 25
 35
(3 rows)
不同用户使用不同脱敏算法
  • 方法一:通过udf实现
seaboxsql=> reset role;
RESET
seaboxsql=# GRANT SELECT ON t2 to u2;
GRANT
seaboxsql=# CREATE FUNCTION custom(int) RETURNS int AS $$
seaboxsql$# begin
seaboxsql$#     if (current_user = 'u1') then
seaboxsql$#     return $1 + 15;
seaboxsql$#     elsif (current_user = 'u2') then
seaboxsql$#         return $1 + 20;
seaboxsql$#     else
seaboxsql$#         return $1;
seaboxsql$#     end if;
seaboxsql$# end;
seaboxsql$# $$ language plpgsql;
CREATE FUNCTION
seaboxsql=# set role sd_sso;
SET
seaboxsql=> ALTER MASKING POLICY p2 ON t2(a) USING EXPRESSION custom(a);
ALTER MASKING POLICY
seaboxsql=# 
seaboxsql=> set role u1;
SET
seaboxsql=> select * from t2;
a  
----
25
35
45
(3 rows)
seaboxsql=# set role u2;
SET
seaboxsql=> select * from t2;
a  
----
30
40
50
(3 rows)
  • 方法二: 通过条件表达式实现
seaboxsql=> reset role;
RESET
seaboxsql=# GRANT SELECT ON t2 to u2;
GRANT
seaboxsql=# set role sd_sso;
SET
seaboxsql=> ALTER MASKING POLICY p2 ON t2(a) USING EXPRESSION case when current_user = 'u1' then a+15 else a+20 end;
ALTER MASKING POLICY
seaboxsql=# 
seaboxsql=> set role u1;
SET
seaboxsql=> select * from t2;
a  
----
25
35
45
(3 rows)
seaboxsql=# set role u2;
SET
seaboxsql=> select * from t2;
a  
----
30
40
50
(3 rows)

注: 可通过上述两种方法实现更加多元的条件脱敏。