试着使用PostgreSQL Anonymizer对数据进行匿名化和屏蔽

本文是2019年PostgreSQL Advent Calendaer的第16天。昨天的文章是由@masayuki038@github分享的“观察PostgreSQL JIT生成的代码”。本文介绍了在PostgreSQL中进行数据匿名化和脱敏处理的扩展工具PostgreSQL Anonymizer。

匿名化和屏蔽的含义是什么?

数据库中包含个人信息和企业机密等需要谨慎处理的数据。如果直接在开发、测试或分析过程中使用此类数据,则存在信息泄漏的风险。在这种情况下,一种安全处理数据的方法是匿名化。

匿名化是指從數據中去除可以識別個人的信息。掩蓋是匿名化的一種方式,即隱藏信息以實現匿名化。然而,匿名化和掩蓋在大多數情況下被用來表示幾乎相同的意思。

PostgreSQL Anonymizer 是什么?

PostgreSQL Anonymizer是一个在PostgreSQL中进行数据匿名化和掩码的扩展。 它由法国的Dalibo公司开发。之前也因为有Ora2Pg的开发者而有名。它的许可证与PostgreSQL相同,都是PostgreSQL许可证。

在PostgreSQL Anonymizer中,通过扩展PostgreSQL的数据定义语言(DDL),定义了数据掩码的规则。通过这样做,可以对数据进行掩码并进行更新,以SQL格式进行转储,并允许用户访问掩码的数据。

PostgreSQL Anonymizer 可与 PostgreSQL 9.6 及更高版本兼容。虽然在版本 9.5 中也可以使用,但是没有提供相关软件包,并且存在一些问题。此外,该项目仍处于开发中阶段,所以最好在了解这一点的基础上使用。

安装PostgreSQL Anonymizer。

如果您使用的是 CentOS 或其他基于 Red Hat 的操作系统,并且已经注册了 Yum 仓库,那么您可以轻松地安装 PostgreSQL Anonymizer,因为它包含在 PostgreSQL 开发者的 Yum 仓库中。关于安装 PostgreSQL 的详细步骤,请参考这篇文章。

在这里,我们假设已经安装了 CentOS 8 并且 PostgreSQL 12 。

首先,安装PostgreSQL Anonymizer 包的 postgresql_anonymizer12 版本。这里的数字12表示 PostgreSQL 的版本。请根据您使用的 PostgreSQL 版本进行相应更改。

# yum -y install postgresql_anonymizer12
(省略)
Installed:
  postgresql_anonymizer12-0.5.0-1.rhel8.x86_64
  perl-Carp-1.42-396.el8.noarch
  perl-Exporter-5.72-396.el8.noarch
  perl-libs-4:5.26.3-416.el8.x86_64
  ddlx_12-0.15-1.rhel8.noarch
  postgresql12-contrib-12.1-2PGDG.rhel8.x86_64

Complete!

安装postgresql_anonymizer12软件包会安装依赖的perl相关、ddlx、postgresql12-contrib软件包。ddlx软件包是从数据库对象中提取DDL的扩展。postgresql12-contrib软件包是PostgreSQL的附加模块,其中需要的是包含的tsm_system_rows扩展。tsm_system_rows扩展是指定从表中进行抽样的最大行数的扩展。perl相关软件包需要一部分附加模块。

然后,编辑PostgreSQL的配置文件postgresql.conf,在启动PostgreSQL服务器时将加载的共享库指定为参数shared_preload_libraries,并用逗号分隔添加表示PostgreSQL Anonymizer的anon。

# - Shared Library Preloading -

shared_preload_libraries = 'anon'       # (change requires restart)
#local_preload_libraries = ''
#session_preload_libraries = ''
#jit_provider = 'llvmjit'               # JIT library to use

为了使参数设置生效,将重新启动PostgreSQL服务。

# systemctl restart postgresql-12.service

最后,为了测试 PostgreSQL Anonymizer,创建并连接数据库 anondb,并安装 PostgreSQL Anonymizer 的扩展 anon,使用anon.load函数加载伪造数据用于数据掩码。

# su - postgres
Last login: Tue Dec  3 21:10:31 UTC 2019 on pts/0
$ createdb anondb
$ psql anondb
=# CREATE EXTENSION anon CASCADE;
NOTICE:  installing required extension "tsm_system_rows"
NOTICE:  installing required extension "ddlx"
CREATE EXTENSION
=# SELECT anon.load();
SELECT
 load
------
 t
(1 行)

=# \q

在执行CREATE EXTENSION命令时指定CASCADE的目的是为了同时安装有依赖关系的扩展。

现在,PostgreSQL Anonymizer 已经安装完成。

遮蔽规则的定义

生成测试数据并定义掩码规则。

首先,我们生成测试数据。创建用于存储部门和员工信息的表departments、employees,并准备一个用于注册数据的SQL文件init.sql。

CREATE TABLE departments (
    id serial PRIMARY KEY,
    name text NOT NULL
);

CREATE TABLE employees (
    id serial PRIMARY KEY,
    last_name text NOT NULL,
    first_name text NOT NULL,
    email text NOT NULL,
    salary int NOT NULL,
    department_id int NOT NULL REFERENCES departments (id),
    birth date NOT NULL
);

INSERT INTO departments (name) VALUES
    ('営業部'),
    ('技術部');

INSERT INTO employees (last_name, first_name, email, salary, department_id, birth) VALUES
    ('佐藤', '太郎', 'sato@example.com', 300000, 1, '1987-01-02'),
    ('鈴木', '次郎', 'suzuki@example.com', 300000, 2, '1988-03-04'),
    ('高橋', '三郎', 'takahashi@example.com', 350000, 1, '1989-05-06'),
    ('田中', '花子', 'tanaka@example.com', 350000, 2, '1990-07-08'),
    ('伊藤', '雪子', 'ito@example.com', 400000, 1, '1991-09-10'),
    ('渡辺', '月子', 'watanabe@example.com', 400000, 2, '1992-11-12');

接下来,将读取并执行 init.sql 文件。

$ psql -f init.sql anondb
CREATE TABLE
CREATE TABLE
INSERT 0 2
INSERT 0 6
$ psql anondb
=# SELECT * FROM departments;
 id |  name
----+--------
  1 | 営業部
  2 | 技術部
(2 行)

=# SELECT * FROM employees;
 id | last_name | first_name |         email         | salary | department_id |   birth
----+-----------+------------+-----------------------+--------+---------------+------------
  1 | 佐藤      | 太郎       | sato@example.com      | 300000 |             1 | 1987-01-02
  2 | 鈴木      | 次郎       | suzuki@example.com    | 300000 |             2 | 1988-03-04
  3 | 高橋      | 三郎       | takahashi@example.com | 350000 |             1 | 1989-05-06
  4 | 田中      | 花子       | tanaka@example.com    | 350000 |             2 | 1990-07-08
  5 | 伊藤      | 雪子       | ito@example.com       | 400000 |             1 | 1991-09-10
  6 | 渡辺      | 月子       | watanabe@example.com  | 400000 |             2 | 1992-11-12
(6 行)

下一步,将定义一个遮蔽规则。规定列名为email的列中存储的员工Eメール地址将通过anon.partial_email函数进行遮蔽。

=# SECURITY LABEL FOR anon ON COLUMN employees.email
-#     IS 'MASKED WITH FUNCTION anon.partial_email(email)';
SECURITY LABEL

使用 SECURITY LABEL 命令来定义掩码规则。 SECURITY LABEL 命令是用来定义安全标签的命令。我之前误以为它是 SELinux 专用命令,但通过指定标签提供程序,可以应用任意访问控制。在 FOR 后面,作为标签提供程序,可以指定 anon,ON COLUMN 后面,作为掩码目标列,可以指定 employees.email,IS 后面,作为标签,可以指定掩码规则。

マスキングルールはanon.partial_email函数的用法。anon.partial_email函数是由PostgreSQL Anonymizer提供的一种掩码函数,它保留电子邮件地址的本地部分、域名的前两个字符和最后一个子域名,并用******替换其余部分。例如,电子邮件地址sato@example.com会被掩码为sa******@ex******.com。

您可以使用系统视图`pg_seclabels`来确认已定义的掩码规则。此外,要删除掩码规则,您可以使用`SECURITY LABEL`命令并指定`NULL`标签来执行。

=# SELECT * FROM pg_seclabels;
 objoid | classoid | objsubid | objtype | objnamespace |     objname     | provider |                     label
--------+----------+----------+---------+--------------+-----------------+----------+------------------------------------------------
  16645 |     1259 |        4 | column  |         2200 | employees.email | anon     | MASKED WITH FUNCTION anon.partial_email(email)
(1 行)

就地匿名化

“原地匿名化(in-place anonymization)是一种将数据进行屏蔽并进行更新的功能。”

要进行原地匿名化,可以使用 anonymize_database、anonymize_table 和 anonymize_column 函数。根据函数名称就可以理解,anonymize_database 函数将对数据库中的所有表进行匿名化,anonymize_table 函数将匿名化指定的表,anonymize_column 函数将匿名化指定表的列。

=# BEGIN;
BEGIN
=# SELECT anon.anonymize_database();
 anonymize_database
--------------------
 t
(1 行)

=# SELECT * FROM employees;
 id | last_name | first_name |         email         | salary | department_id |   birth
----+-----------+------------+-----------------------+--------+---------------+------------
  1 | 佐藤      | 太郎       | sa******@ex******.com | 300000 |             1 | 1987-01-02
  2 | 鈴木      | 次郎       | su******@ex******.com | 300000 |             2 | 1988-03-04
  3 | 高橋      | 三郎       | ta******@ex******.com | 350000 |             1 | 1989-05-06
  4 | 田中      | 花子       | ta******@ex******.com | 350000 |             2 | 1990-07-08
  5 | 伊藤      | 雪子       | it******@ex******.com | 400000 |             1 | 1991-09-10
  6 | 渡辺      | 月子       | wa******@ex******.com | 400000 |             2 | 1992-11-12
(6 行)

=# ROLLBACK;
ROLLBACK
=# \q

另外,原地匿名化会更新数据本身,导致原始数据丢失。而且,由于需要更新表中的所有数据,执行过程可能需要很长时间。在后续的匿名备份中,对数据进行掩码处理并进行备份,然后进行还原可能会更快。

匿名转储

匿名转储是一种能够将数据掩盖并以SQL格式导出的功能。

要执行匿名转储,请执行anon.dump函数。如果要转储到文件中,请在命令行中执行函数,并将其输出重定向。在此过程中,为了抑制多余的消息、对齐和除行外的输出,需指定-qAt选项。

$ psql -qAt -c "SELECT anon.dump()" anondb
(省略)
 COPY departments
                              FROM STDIN
                              CSV QUOTE AS '"'
                              DELIMITER ',';
1,営業部
2,技術部
\.

 COPY employees
                              FROM STDIN
                              CSV QUOTE AS '"'
                              DELIMITER ',';
1,佐藤,太郎,sa******@ex******.com,300000,1,1987-01-02
2,鈴木,次郎,su******@ex******.com,300000,2,1988-03-04
3,高橋,三郎,ta******@ex******.com,350000,1,1989-05-06
4,田中,花子,ta******@ex******.com,350000,2,1990-07-08
5,伊藤,雪子,it******@ex******.com,400000,1,1991-09-10
6,渡辺,月子,wa******@ex******.com,400000,2,1992-11-12
\.

动态遮罩

动态遮蔽是一种动态地对数据进行遮蔽,并允许用户访问的功能。通过在另一个模式下定义一个具有相同名称的视图,并应用遮蔽函数,然后切换模式以允许用户访问,从而实现此功能。

首先,执行anon.start_dynamic_masking函数以启用动态屏蔽。

=# SELECT anon.start_dynamic_masking();
 start_dynamic_masking
-----------------------
 t
(1 行)

接下来,我们将创建一个名为masked_user的用户,让他可以访问被蒙面的数据。

=# CREATE ROLE masked_user LOGIN;
CREATE ROLE
=# SECURITY LABEL FOR anon ON ROLE masked_user IS 'MASKED';
SECURITY LABEL
=# GRANT SELECT ON ALL TABLES IN SCHEMA mask TO masked_user;
GRANT
=# \q

使用CREATE ROLE命令创建具有LOGIN权限的用户,并使用SECURITY LABEL命令赋予其MASKED标签。带有MASKED标签的用户访问将自动切换到包含被掩盖表的模式mask。访问权限需要手动设置。使用GRANT命令授予mask模式中所有表的SELECT权限。

最后,将作为masked_user用户连接到anondb数据库,并验证数据已被掩码处理。

$ psql -U masked_user anondb
=> SELECT * FROM employees;
 id | last_name | first_name |         email         | salary | department_id |   birth
----+-----------+------------+-----------------------+--------+---------------+------------
  1 | 佐藤      | 太郎       | sa******@ex******.com | 300000 |             1 | 1987-01-02
  2 | 鈴木      | 次郎       | su******@ex******.com | 300000 |             2 | 1988-03-04
  3 | 高橋      | 三郎       | ta******@ex******.com | 350000 |             1 | 1989-05-06
  4 | 田中      | 花子       | ta******@ex******.com | 350000 |             2 | 1990-07-08
  5 | 伊藤      | 雪子       | it******@ex******.com | 400000 |             1 | 1991-09-10
  6 | 渡辺      | 月子       | wa******@ex******.com | 400000 |             2 | 1992-11-12
(6 行)

=> \q

因此,由于动态掩码是通过切换架构来实现的,所以只能将掩码目标架构限制在一个。默认情况下是公共架构,可以在anon.start_dynamic_masking函数的第一个参数中进行指定。此外,还可以通过第二个参数来指定存储掩码表的架构。


PostgreSQL Anonymizer 的介绍到此为止。虽然本次没有介绍,但是还有其他的掩码函数可用,包括添加噪音、乱序、随机化、伪装、部分混淆、通用化和k-匿名化等。需要注意的是,仅准备了英语的伪装数据,但也可以使用CSV格式创建和加载日语的伪装数据。

广告
将在 10 秒后关闭
bannerAds