数据脱敏
GaussDB(DWS)提供列级别的数据脱敏(Dynamic Data Masking)功能。针对某些敏感信息(如身份证号、手机号、银行卡号等),通过应用脱敏函数进行原始数据的变形改写,实现敏感隐私数据的可靠保护,从而增强产品在数据安全和隐私保护方面的能力。
- 创建表的数据脱敏策略
GaussDB(DWS)通过使用CREATE REDACTION POLICY语法在表上创建数据脱敏策略并使用函数(MASK_NONE,不进行任何脱敏处理;MASK_FULL,全脱敏成固定值;MASK_PARTIAL,按指定的字符类型,数值类型或时间类型进行部分脱敏处理。)限定脱敏策略生效范围。
- 修改表的数据脱敏策略
ALTER REDACTION POLICY语法用于修改脱敏策略生效表达式、重命名脱敏策略,以及新增、修改、删除脱敏列信息。
- 删除表的脱敏策略
DROP REDACTION POLICY语法用于删除脱敏策略在表的所有列字段上的脱敏函数信息。
- 查看脱敏策略和脱敏列信息
脱敏策略信息存储在系统表PG_REDACTION_POLICY,脱敏列信息存储在系统表PG_REDACTION_COLUMN。用户也可以通过系统视图REDACTION_POLICIES和REDACTION_COLUMNS,更加方便地查看脱敏策略及脱敏列信息。
- 通常,用户可以执行SELECT语句查看敏感信息的脱敏效果。如果语句有如下特征,则可能存在故意套取敏感数据的可能性,造成语句执行报错。
- GROUP BY子句引用与目标列一样含有脱敏列的Target Entry作为分组。
- DISTINCT作用在输出的脱敏列上。
- 带有CTE的语句。
- 涉及集合操作。
- 子查询的目标列不是基表的脱敏列,而是基表脱敏列的表达式或者函数调用。
- 支持脱敏数据的COPY TO或者GDS导出功能。由于脱敏数据的不可逆性,针对脱敏数据的二次运算无任何实际意义。
- 禁止UPDATE、MERGE INTO、DELETE语句的目标列涉及脱敏列。
- UPSERT语句允许通过EXCLUDED更新插入数据。如果引用脱敏列更新基表数据,存在误改数据的可能,执行会报错。
- 8.2.1集群版本针对同一张表可以允许创建多个脱敏策略,实现敏感数据分级分类场景下的多样化配置。多脱敏策略的选择和应用原则如下:
- 选择满足当前Session的多个候选策略中policy_order最大的那一个策略,即策略次序越大表示越晚创建。
- 脱敏可算场景下,DML语句发生策略的继承,也仅继承源表相关的候选策略中policy_order最大的那一个策略。
示例
以员工表emp,表的属主alice及角色matu、july为例,简要介绍数据脱敏过程。其中,表emp包含员工的姓名、手机号、邮箱、银行卡号、薪资等隐私数据
- 使用管理员用户连接数据库后,创建角色alice、matu和july。
1 2 3
CREATE ROLE alice PASSWORD 'password'; CREATE ROLE matu PASSWORD 'password'; CREATE ROLE july PASSWORD 'password';
- 赋予alice、matu和july当前数据库的模式权限。
1
GRANT ALL PRIVILEGES on schema public to alice,matu,july;
- 切换至角色alice,创建表emp并插入三条员工信息。
1 2 3 4 5 6 7
SET ROLE alice PASSWORD 'password'; CREATE TABLE emp(id int, name varchar(20), phone_no varchar(11), card_no number, card_string varchar(19), email text, salary numeric(100, 4), birthday date); INSERT INTO emp VALUES(1, 'anny', '13420002340', 1234123412341234, '1234-1234-1234-1234', 'smithWu@163.com', 10000.00, '1999-10-02'); INSERT INTO emp VALUES(2, 'bob', '18299023211', 3456345634563456, '3456-3456-3456-3456', '66allen_mm@qq.com', 9999.99, '1989-12-12'); INSERT INTO emp VALUES(3, 'cici', '15512231233', NULL, NULL, 'jonesishere@sina.com', NULL, '1992-11-06');
- alice将表emp的读取权限授予matu、july。
1
GRANT SELECT ON emp TO matu, july;
- 创建脱敏策略mask_emp,仅alice可查看员工所有信息,matu和july对员工银行卡号和薪资数据不可见。字段card_no是数值类型,采用MASK_FULL全脱敏成固定值0;字段card_string是字符类型,采用MASK_PARTIAL按指定的输入输出格式对原始数据进行部分脱敏;字段salary是数值类型,采用MASK_PARTIAL指定数字9部分脱敏倒数第二位前的所有数位值。
1 2 3 4
CREATE REDACTION POLICY mask_emp ON emp WHEN (current_user IN ('matu', 'july')) ADD COLUMN card_no WITH mask_full(card_no), ADD COLUMN card_string WITH mask_partial(card_string, 'VVVVFVVVVFVVVVFVVVV','VVVV-VVVV-VVVV-VVVV','#',1,12), ADD COLUMN salary WITH mask_partial(salary, '9', 1, length(salary) - 2);
- 切换到matu和july,查看员工表emp。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
SET ROLE matu PASSWORD 'password'; SELECT * FROM emp; id | name | phone_no | card_no | card_string | email | salary | birthday ----+------+-------------+---------+---------------------+----------------------+------------+--------------------- 1 | anny | 13420002340 | 0 | ####-####-####-1234 | smithWu@163.com | 99999.9990 | 1999-10-02 00:00:00 2 | bob | 18299023211 | 0 | ####-####-####-3456 | 66allen_mm@qq.com | 9999.9990 | 1989-12-12 00:00:00 3 | cici | 15512231233 | | | jonesishere@sina.com | | 1992-11-06 00:00:00 (3 rows) SET ROLE july PASSWORD 'password'; SELECT * FROM emp; id | name | phone_no | card_no | card_string | email | salary | birthday ----+------+-------------+---------+---------------------+----------------------+------------+--------------------- 1 | anny | 13420002340 | 0 | ####-####-####-1234 | smithWu@163.com | 99999.9990 | 1999-10-02 00:00:00 2 | bob | 18299023211 | 0 | ####-####-####-3456 | 66allen_mm@qq.com | 9999.9990 | 1989-12-12 00:00:00 3 | cici | 15512231233 | | | jonesishere@sina.com | | 1992-11-06 00:00:00 (3 rows)
- 若需要matu也有员工所有信息的查看权限,只有july不可见,修改策略生效范围即可。
1 2
SET ROLE alice PASSWORD 'password'; ALTER REDACTION POLICY mask_emp ON emp WHEN(current_user = 'july');
- 切换到matu和july,重新查看员工表emp。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
SET ROLE matu PASSWORD 'password'; SELECT * FROM emp; id | name | phone_no | card_no | card_string | email | salary | birthday ----+------+-------------+------------------+---------------------+----------------------+------------+--------------------- 1 | anny | 13420002340 | 1234123412341234 | 1234-1234-1234-1234 | smithWu@163.com | 10000.0000 | 1999-10-02 00:00:00 2 | bob | 18299023211 | 3456345634563456 | 3456-3456-3456-3456 | 66allen_mm@qq.com | 9999.9900 | 1989-12-12 00:00:00 3 | cici | 15512231233 | | | jonesishere@sina.com | | 1992-11-06 00:00:00 (3 rows) SET ROLE july PASSWORD 'password'; SELECT * FROM emp; id | name | phone_no | card_no | card_string | email | salary | birthday ----+------+-------------+---------+---------------------+----------------------+------------+--------------------- 1 | anny | 13420002340 | 0 | ####-####-####-1234 | smithWu@163.com | 99999.9990 | 1999-10-02 00:00:00 2 | bob | 18299023211 | 0 | ####-####-####-3456 | 66allen_mm@qq.com | 9999.9990 | 1989-12-12 00:00:00 3 | cici | 15512231233 | | | jonesishere@sina.com | | 1992-11-06 00:00:00 (3 rows)
- 员工信息phone_no、email和birthday也是隐私数据,更新脱敏策略mask_emp,新增三个脱敏列。
1 2 3 4
SET ROLE alice PASSWORD 'password'; ALTER REDACTION POLICY mask_emp ON emp ADD COLUMN phone_no WITH mask_partial(phone_no, '*', 4); ALTER REDACTION POLICY mask_emp ON emp ADD COLUMN email WITH mask_partial(email, '*', 1, position('@' in email)); ALTER REDACTION POLICY mask_emp ON emp ADD COLUMN birthday WITH mask_full(birthday);
- 切换到july,查看表emp数据。
1 2 3 4 5 6 7 8
SET ROLE july PASSWORD 'password'; SELECT * FROM emp; id | name | phone_no | card_no | card_string | email | salary | birthday ----+------+-------------+---------+---------------------+----------------------+------------+--------------------- 1 | anny | 134******** | 0 | ####-####-####-1234 | ********163.com | 99999.9990 | 1970-01-01 00:00:00 2 | bob | 182******** | 0 | ####-####-####-3456 | ***********qq.com | 9999.9990 | 1970-01-01 00:00:00 3 | cici | 155******** | | | ************sina.com | | 1970-01-01 00:00:00 (3 rows)
- 通过视图redaction_policies和redaction_columns查看当前脱敏策略mask_emp的详细信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
SELECT * FROM redaction_policies; object_schema | object_owner | object_name | policy_name | expression | enable | policy_description | inherited ---------------+--------------+-------------+-------------+-----------------------------------+--------+--------------------+----------- public | alice | emp | mask_emp | ("current_user"() = 'july'::name) | t | | f (1 row) SELECT object_name, column_name, function_info FROM redaction_columns; object_name | column_name | function_info -------------+-------------+------------------------------------------------------------------------------------------------------- emp | card_no | mask_full(card_no) emp | card_string | mask_partial(card_string, 'VVVVFVVVVFVVVVFVVVV'::text, 'VVVV-VVVV-VVVV-VVVV'::text, '#'::text, 1, 12) emp | email | mask_partial(email, '*'::text, 1, "position"(email, '@'::text)) emp | salary | mask_partial(salary, '9'::text, 1, (length((salary)::text) - 2)) emp | birthday | mask_full(birthday) emp | phone_no | mask_partial(phone_no, '*'::text, 4) (6 rows)
- 新增一列salary_info,若需要将文本类型的薪资信息统一脱敏成“*.*”,可以创建自定义脱敏函数实现。此处采用PL/PGSQL语言定义脱敏函数mask_regexp_salary,创建脱敏列时,只需自定义脱敏的函数名和参数列表,详细内容可参考用户自定义函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
SET ROLE alice PASSWORD 'password'; ALTER TABLE emp ADD COLUMN salary_info TEXT; UPDATE emp SET salary_info = salary::text; CREATE FUNCTION mask_regexp_salary(salary_info text) RETURNS text AS $$ SELECT regexp_replace($1, '[0-9]+','*','g'); $$ LANGUAGE SQL STRICT SHIPPABLE; ALTER REDACTION POLICY mask_emp ON emp ADD COLUMN salary_info WITH mask_regexp_salary(salary_info); SET ROLE july PASSWORD 'password'; SELECT id, name, salary_info FROM emp; id | name | salary_info ----+------+------------- 1 | anny | *.* 2 | bob | *.* 3 | cici | (3 rows)
- 无需为表emp设置敏感策略,删除脱敏策略mask_emp。
1 2
SET ROLE alice PASSWORD 'password'; DROP REDACTION POLICY mask_emp ON emp;