openGauss 2.0版本已经内置动态脱敏的特性,相关特性介绍可以参考我写过的这篇文章:<<openGauss数据动态脱敏之不同策略测试>>
PostgreSQL内核目前还不支持里这个特性,但gitlab上有个插件Anonymizer支持该特性,本文参考官方文档进行简单的测试。
1.安装部署Anonymizer
https://postgresql-anonymizer.readthedocs.io/en/latest/INSTALL/#install-from-source
参考上面源码编译方式,本地编译示例如下:
$ make cp anon.sql anon/anon--1.1.0.sql cp data/*.csv anon/ cp python/populate.py anon/
复制
$ make install cp anon.sql anon/anon--1.1.0.sql cp data/*.csv anon/ cp python/populate.py anon/ /usr/bin/mkdir -p '/opt/pg15/share/postgresql/extension' /usr/bin/mkdir -p '/opt/pg15/share/postgresql/extension/anon' /usr/bin/mkdir -p '/opt/pg15/lib/postgresql' install -d /opt/pg15/bin install -m 0755 bin/pg_dump_anon.sh /opt/pg15/bin /usr/bin/install -c -m 644 .//anon.control '/opt/pg15/share/postgresql/extension/' /usr/bin/install -c -m 644 .//anon/* '/opt/pg15/share/postgresql/extension/anon/' /usr/bin/install -c -m 755 anon.so '/opt/pg15/lib/postgresql/'
复制
数据库级别配置动态库
create database foo; alter database foo set session_preload_libraries = 'anon';
复制
在示例数据库foo中创建anon扩展插件,anon依赖加密插件pgcrypto,需要先创建pgcrypto:
\c foo create extension pgcrypto; create extension anon;
复制
创建完后检查扩展插件
foo=# \dx List of installed extensions Name | Version | Schema | Description ----------+---------+------------+------------------------------ anon | 1.1.0 | public | Data anonymization tools pgcrypto | 1.3 | public | cryptographic functions plpgsql | 1.0 | pg_catalog | PL/pgSQL procedural language (3 rows)
复制
如果需要使用伪造函数,需要执行下面的初始化函数,生成伪造数据。
SELECT anon.init();
复制
2.anon参数设置
anon插件主要支持如下参数的设置:
- anon.algorithm:脱敏函数使用的hash算法,默认为sha256,支持md5、sha256、sha384和sha512等,需要超级用户权限才可设置。
- anon.salt:脱敏函数可使用的salt值,一般建议每个数据库设置不一样的值,需要超级用户权限才可设置。
- anon.maskschema:动态脱敏视图存储的模式名称,默认名称为mask。
- anon.sourceshema:动态脱敏引擎对表数据生效的schema源,默认是public模式。
- anon.restrict_to_trusted_schemas:可限制脱敏规则必须在指定的schema范围内来提升安全性,默认是off。
进行如下设置:
alter database foo set anon.restrict_to_trusted_schemas = on; alter database foo set anon.salt = 'db_foo_Secret_Salt';
复制
重新连接数据库后执行如下语句:
select anon.start_dynamic_masking();
复制
3.配置脱敏规则
数据脱敏规则通过security labels进行声明:
CREATE TABLE public.player(id SERIAL, name TEXT, points INT); INSERT INTO public.player VALUES ( 1, 'Kareem Abdul-Jabbar', 38387), ( 5, 'Michael Jordan', 32292 ); SECURITY LABEL FOR anon ON COLUMN player.name IS 'MASKED WITH FUNCTION anon.fake_last_name()'; SECURITY LABEL FOR anon ON COLUMN player.id IS 'MASKED WITH VALUE NULL';
复制
如果安全标签声明内容里包含字符串标量,可以使用C风格的转义或使用双美元符号。
使用C风格转义:
SECURITY LABEL FOR anon ON COLUMN player.name IS E'MASKED WITH VALUE \'CONFIDENTIAL\'';
复制
或使用双美元符:
SECURITY LABEL FOR anon ON COLUMN player.name IS 'MASKED WITH VALUE $$CONFIDENTIAL$$';
复制
使用表达式
SECURITY LABEL FOR anon ON COLUMN player.name IS 'MASKED WITH VALUE CASE WHEN name IS NULL THEN $$John$$ ELSE anon.random_string(LENGTH(name)) END';
复制
移除单个列的脱敏规则
SECURITY LABEL FOR anon ON COLUMN player.name IS NULL;
复制
移除所有表的脱敏规则
SELECT anon.remove_masks_for_all_columns();
复制
注意:分区表需要在分区上独立进行设置。
4.脱敏函数
anon提供了8种脱敏策略,可以对不同的列分别使用不同的脱敏策略:
- Destruction(重构)
- Adding Noise(噪音化)
- Randomization(随机化)
- Faking(伪造)/Advanced Faking(高级伪造)
- Pseudonymization(笔名化)
- Generic Hashing(哈希)
- Partial scrambling(部分遗漏)
- Generalization((泛化))
Destruction(重构):替换为单个静态值
SECURITY LABEL FOR anon ON COLUMN users.address IS 'MASKED WITH VALUE ''CONFIDENTIAL'' ';
复制
Adding Noise(噪音化):它是对日期和数值添加一个方差,有两种函数形式:
- anon.noise(original_value,ratio) 其中original_value可以是integer、bigint或double precision。如果ratio为0.33,返回值将是原始值的33%上下波动。
- anon.dnoise(original_value, interval)其中original_value可以是日期、时间戳或时间。如果间隔为2天,返回值将是原始值前后2天波动。
Randomization(随机化)
基本随机值
- anon.random_date()返回日期
- anon.random_string(n)返回包含n个字母的文本值
- anon.random_zip()返回一个5位代码
- anon.random_phone§返回一个以p为前缀的手机号
- anon.random_hash(seed)返回给定种子的随机字符串
边界随机值
- anon.random_date_between(d1,d2)返回d1和d2之间的日期
- anon.random_int_between(i1,i2)返回i1和i2之间的整数
- anon.random_bigint_between(b1,b2)返回b1和b1之间的大整数
基于数组的随机值
- anon.random_in(ARRAY[1,2,3])返回1到3之间的整数
- anon.random_in(ARRAY[‘red’,‘green’,‘blue’])返回文本
Faking(伪造)/Advanced Faking(高级伪造):伪造的想法是用随机但合理的值取代敏感数据。
为了使用伪造函数,需要先调用init函数
SELECT anon.init();
复制
加载数据后,可以访问以下伪造函数:
- anon.fake_address()返回完整的邮寄地址
- anon.fake_city()返回城市名称
- anon.fake_country()返回国家名称
- anon.fake_company()返回公司名称
- anon.fake_email()返回有效的电子邮件地址
- anon.fake_first_name()返回名字
- anon.fake_iban()返回有效的IBAN
- anon.fake_last_name()返回姓氏
- anon.fake_postcode()返回有效的邮政编码
- anon.fake_siret()返回有效的SIRET
高级伪造请查看:PostgreSQL Faker
Pseudonymization(笔名化):类似于伪造,主要区别在于笔名化是确定性的,相同的seed和salt返回相同的值。
为了使用笔名化函数,需要先调用init函数
SELECT anon.init();
复制
加载数据后,可以访问以下函数:
- anon.pseudo_first_name(‘seed’,‘salt’)返回名字
- anon.pseudo_last_name(‘seed’,‘salt’)返回姓氏
- anon.pseudo_email(‘seed’,‘salt’)返回有效的电子邮件地址
- anon.pseudo_city(‘seed’,‘salt’)返回城市名称
- anon.pseudo_country(‘seed’,‘salt’)返回国家名称
- anon.pseudo_company(‘seed’,‘salt’)返回公司名称
- anon.pseudo_iban(‘seed’,‘salt’)返回有效的IBAN
- anon.pseudo_siret(‘seed’,‘salt’)返回有效的SIRET
第二个参数salt是可选的,salt可以增加复杂性,避免字典和暴力攻击。seed可以是与主题相关的任何信息。例如,我们可以始终使用给定的登录名作为seed为给定的人生成相同的虚假电子邮件地址。
SECURITY LABEL FOR anon ON COLUMN users.emailaddress IS 'MASKED WITH FUNCTION anon.pseudo_email(users.login)';
复制
Generic Hashing(哈希)
- anon.set_secret_salt(value) 自定义salt值,初始化扩展时会生成随机salt值。
- anon.set_algorithm(value) 自定义哈希算法,可选值为:md5、sha1、sha224、sha256、sha384或sha512,默认值哈希算法是sha256。
- anon.hash(value) 使用salt和哈希算法计算hash值。
- anon.digest(value,salt,algorithm) 允许从预定义的列表中选择salt和哈希算法。
Partial scrambling(部分遗漏)
例如掩盖信用卡中间号码或邮箱地址
- anon.partial(‘4012 3452 4568 2396’,2,‘XXXX’,3)
- anon.partial_email(‘daamien@gmail.com’)
Generalization(泛化):泛化是将原始值替换为包含该值的范围。例如,与其说“保罗42岁”,不如说“保罗在40到50岁之间”。
- generalize_int4range(value, step)
- generalize_int8range(value, step)
- generalize_numrange(value, step)
- generalize_daterange(value, step)
- generalize_tsrange(value, step)
- generalize_tstzrange(value, step)
其中value是将要泛化的数据,step是每个范围的步长。
除了上面的八种策略,我们还可以自定义脱敏函数,例如在模式bar内定义脱敏函数foo():
SECURITY LABEL FOR anon ON SCHEMA bar IS 'TRUSTED'; SECURITY LABEL FOR anon ON COLUMN player.score IS 'MASKED WITH FUNCTION bar.foo()';
复制
下面的示例,对JSON类型进行自定义脱敏处理。
SECURITY LABEL FOR anon ON SCHEMA custom_masks IS 'TRUSTED'; CREATE FUNCTION custom_masks.remove_last_name(j JSONB) RETURNS JSONB VOLATILE LANGUAGE SQL AS $func$ SELECT json_build_object( 'employees' , array_agg( jsonb_set(e ,'{lastName}', to_jsonb(anon.fake_last_name())) ) )::JSONB FROM jsonb_array_elements( j->'employees') e $func$;
复制
5.静态脱敏
静态脱敏会损坏原始数据,使用时需要额外小心。
定义完脱敏规则后使用下面的函数替换真实数据。
select anon.anonymize_database()
复制
也可使用下面的函数进行局部处理:
SELECT anon.anonymize_table('customer'); SELECT anon.anonymize_column('customer','zipcode');
复制
6.动态脱敏
我们可以将脱敏数据放到一个脱敏用户下,其他用户仍然可以访问原始数据。
示例:
CREATE TABLE people (id TEXT, firstname TEXT, lastname TEXT, phone TEXT); INSERT INTO people VALUES ('T1','Sarah', 'Conor','0609110911');
复制
步骤一:激活动态脱敏引擎
CREATE EXTENSION IF NOT EXISTS anon CASCADE; SELECT anon.start_dynamic_masking();
复制
步骤二:定义脱敏用户
CREATE ROLE skynet LOGIN; SECURITY LABEL FOR anon ON ROLE skynet IS 'MASKED';
复制
步骤三:定义脱敏规则
SECURITY LABEL FOR anon ON COLUMN people.lastname IS 'MASKED WITH FUNCTION anon.random_string(4)'; SECURITY LABEL FOR anon ON COLUMN people.phone IS 'MASKED WITH FUNCTION anon.partial(phone,2,$$******$$,2)';
复制
步骤四:使用脱敏用户查询
\c - skynet foo=> select * from people; id | firstname | lastname | phone ----+-----------+----------+------------ T1 | Sarah | ROKS | 06******11 (1 row)
复制
更改脱敏列的数据类型:当动态脱敏引擎被激活时,如果列上有脱敏规则,则不允许更改列的数据类型。
修改脱敏列需要临时停止脱敏引擎:
BEGIN; SELECT anon.stop_dynamic_masking(); ALTER TABLE people ALTER COLUMN phone TYPE VARCHAR(255); SELECT anon.start_dynamic_masking(); COMMIT;
复制
删除脱敏表:动态脱敏引擎将在脱敏表上构建脱敏视图,直接删除脱敏表会提示这样的错误:
ERROR: cannot drop table people because other objects depend on it DETAIL: view mask.people depends on table people HINT: Use DROP ... CASCADE to drop the dependent objects too.
复制
需要使用CASCADE选项,级联删除脱敏视图。
DROP TABLE people CASCADE;
复制
7.脱敏数据导出
如果需要使用脱敏用户导出数据,需要使用工具pg_dump_anon,工具安装参考:
https://postgresql-anonymizer.readthedocs.io/en/latest/anonymous_dumps/#with-go
8.抽样脱敏
如果是测试目的,仅提取和脱敏10%的数据,示例如下:
CREATE TABLE http_logs ( id integer NOT NULL, date_opened DATE, ip_address INET, url TEXT ); SECURITY LABEL FOR anon ON COLUMN http_logs.ip_address IS 'MASKED WITH VALUE NULL'; SECURITY LABEL FOR anon ON TABLE http_logs IS 'TABLESAMPLE BERNOULLI(10)';
复制
也可以在数据库级别定义抽样:
SECURITY LABEL FOR anon ON DATABASE foo IS 'TABLESAMPLE SYSTEM(33)';
复制
9.术语定义
https://postgresql-anonymizer.readthedocs.io/en/latest/concepts/
- Dynamic Masking
- Permanent Destruction
- Deletion/Nullification
- Static Substitution
- Variance
- Generalization
- Shuffling
- Randomization
- Partial scrambling
- Custom rules
- Pseudonymization
10.相关链接
官方链接
https://gitlab.com/dalibo/postgresql_anonymizer/
官方文档
https://postgresql-anonymizer.readthedocs.io/en/stable/
目前该功能还处于测试阶段,作者建议2.0版本才可用于生产,敬请关注。
保持联系
本人组建了一个粉丝群:PG乐知乐享交流群。欢迎关注文章的小伙伴随缘加入,进群请加微信并备注PG乐知乐享。
文章被以下合辑收录
评论
