2025年9月25日: PostgreSQL 18 发布!

pg_anonymize,一个用于简单透明数据匿名化的新扩展

发布于 2023-03-09,作者 Julien Rouhaud
相关开源项目

台湾台北 - 2023 年 3 月 9 日

pg_anonymize,一个用于简单透明数据匿名化的新扩展

很高兴宣布 pg_anonymize 的 beta 版本。

pg_anonymize 是一个 PostgreSQL 扩展,它为数据匿名化提供了一个简单、健壮且透明的基础设施。其目标是确保任何连接到已匿名化角色的用户只能看到数据的匿名化版本,而不会受到所用客户端(可以是 psql、pg_dump、您自己的应用程序甚至是 pg_sample 等其他工具)或模式和关系数量的限制。

匿名化是使用声明式方法完成的,依赖于 SECURITY LABELs:您只需为每个需要匿名化的列声明一个表达式,该表达式将动态执行匿名化。可以使用任何生成正确类型的有效表达式,因此您可以使用纯 SQL、plpgsql 或您选择的任何其他过程语言来编写它们。

此扩展与 PostgreSQL 10 及更高版本兼容。有关其用法的更多详细信息,请参阅下面的 用法 部分或 文档

如果您遇到任何问题或想申请新功能,请随时 提出 issue

配置

pg_anonymize 提供以下配置选项

  • pg_anonymize.enabled (bool):允许全局启用或禁用 pg_anonymize。默认值为 on

  • pg_anonymize.check_labels (bool):在声明安全标签时,对定义的表达式执行健全性检查(表达式有效性、只读、返回类型和缺乏 SQL 注入)。默认值为 on

注意:即使 pg_anonymize.check_labels 被禁用,pg_anonymize 仍会检查定义的表达式是否不包含任何 SQL 注入。

用法

在使用 pg_anonymize 之前必须加载它。有多种方法可以做到这一点。通常,只有少数角色需要数据匿名化,因此推荐的方法是仅为这些角色加载扩展。例如,假设角色 alice 需要使用

ALTER ROLE alice SET session_preload_libraries = 'pg_anonymize';

注意:只有在成功运行此命令 之后 由 alice 打开的会话才会加载 pg_anonymize。

您也可以显式加载它,例如

LOAD 'pg_anonymize';

注意:LOAD 需要超级用户权限。

然后,您需要将所需的角色声明为需要匿名化数据。这是通过向目标角色添加 anonymize 的 SECURITY LABEL 来完成的。例如

-- pg_anonymize 需要在声明 SECURITY LABEL 之前加载 LOAD 'pg_anonymize'; SECURITY LABEL FOR pg_anonymize ON ROLE alice IS 'anonymize';

注意:在角色上声明 SECURITY LABEL 需要 CREATEROLE 权限。

然后,您可以使用 SECURITY LABELS 声明如何匿名化每个列,定义一个表达式来替换实际内容。

例如,假设有一个简单的客户表

``` CREATE TABLE public.customer(id integer, first_name text, last_name text, birthday date, phone_number text);

INSERT INTO public.customer VALUES (1, 'Nice', 'Customer', '1970-03-04', '+886 1234 5678');

GRANT SELECT ON TABLE public.customer TO alice; ```

让我们匿名化姓氏、生日和电话号码

SECURITY LABEL FOR pg_anonymize ON COLUMN public.customer.last_name IS $$substr(last_name, 1, 1) || '*****'$$; SECURITY LABEL FOR pg_anonymize ON COLUMN public.customer.birthday IS $$date_trunc('year', birthday)::date$$; SECURITY LABEL FOR pg_anonymize ON COLUMN public.customer.phone_number IS $$regexp_replace(phone_number, '\d', 'X', 'g')$$;

注意:在列上声明 SECURITY LABEL 需要成为底层关系的属主。

alice 角色现在将自动看到匿名化数据。例如

``` -- 当前角色看到正常数据 =# SELECT * FROM public.customer; id | first_name | last_name | birthday | phone_number ----+------------+-----------+------------+---------------- 1 | Nice | Customer | 1970-03-04 | +886 1234 5678 (1 row)

-- 但 alice 将看到匿名化数据 =# \c - alice 您现在已连接到数据库 "rjuju" 作为用户 "alice"。

=> SELECT * FROM public.customer; id | first_name | last_name | birthday | phone_number ----+------------+-----------+------------+---------------- 1 | Nice | C* | 1970-01-01 | +XXX XXXX XXXX (1 row)

-- pg_dump 也将看到匿名化数据 $ pg_dump -U alice -t public.customer -a rjuju | grep "COPY" -A2 COPY public.customer (id, first_name, last_name, birthday, phone_number) FROM stdin; 1 Nice C* 1970-01-01 +XXX XXXX XXXX . ```