G.1. pgpro_anonymizer
- G.1.1. Термины и определения
- G.1.2. Пример анонимизации
- G.1.3. Установка и подготовка
- G.1.4. Конфигурирование
- G.1.5. Объявление правил маскирования
- G.1.6. Функции маскирования
- G.1.7. Статическое маскирование
- G.1.8. Динамическое маскирование
- G.1.9. Выгрузка анонимизированных данных
- G.1.10. Обобщение
- G.1.11. Производительность
- G.1.12. Безопасность
- G.1.13. Извлечение выборки
- G.1.2. Пример анонимизации
pgpro_anonymizer — это расширение для маскирования или замены конфиденциальных коммерческих данных или информации, позволяющей установить личность (PII, персональные данные), в БД Postgres Pro.
В проекте используется декларативный подход к анонимизации. Это означает, что вы можете объявлять правила маскирования, используя язык описания данных (DDL), и задавать свою стратегию анонимизации внутри самого определения таблицы.
После определения правил маскирования вы можете получить доступ к анонимизированным данным следующими способами:
Выгрузка анонимизированных данных: простое экспортирование замаскированных данных в файл SQL.
Статическое маскирование: удаление персональных данных в соответствии с правилами.
Динамическое маскирование: скрытие персональных данных только от недоверенных пользователей.
Кроме того, доступны различные функции для реализации следующих методов маскирования: рандомизация, фальсификация, частичное скрытие, перестановка и искажение. Пользователи могут также разрабатывать свои функции.
Помимо маскирования также можно использовать ещё один подход, который называется обобщение и полезен для сбора статистики и анализа данных.
G.1.1. Термины и определения
Используются следующие основные стратегии:
Динамическое маскирование изменяет представление реальных данных, не модифицируя их. Некоторые пользователи могут читать только замаскированные данные, а другие могут получить доступ к исходной версии.
Статическое маскирование полностью заменяет конфиденциальную информацию несвязанными данными. После такой обработки исходные данные не могут быть восстановлены.
Данные могут быть изменены несколькими способами:
Удаление просто удаляет данные.
Статическая замена последовательно заменяет данные одним и тем же значением. Например: замена всех значений столбца типа
textна значение «КОНФИДЕНЦИАЛЬНО».Отклонение «сдвигает» даты и числовые значения. Например, после применения отклонения +/- 10% к столбцу зарплаты набор данных не потеряет смысл.
Обобщение снижает точность данных, заменяя их диапазоном значений. Вместо «Бобу 28 лет» можно сказать «Бобу от 20 до 30 лет». Этот метод полезен для аналитики, так как данные остаются верными.
Перестановка перемешивает значения в рамках столбца. Исходные данные могут быть восстановлены, если алгоритм перестановки будет расшифрован.
Рандомизация заменяет конфиденциальные данные случайными, но правдоподобными значениями. Цель состоит в том, чтобы исключить возможность какой-либо идентификации по записи данных, оставляя её пригодной для тестирования, анализа и обработки данных.
Частичное скрытие аналогично статической подстановке, но оставляет часть данных нетронутыми. Например: номер кредитной карты может быть заменён на «40XX XXXX XXXX XX96».
Пользовательские правила предназначены для изменения данных в соответствии с особыми требованиями (например, заменить одновременно почтовый индекс и название города случайными значениями, чтобы они оставались согласованными).
Псевдонимизация защищает персональные данные, скрывая их с помощью дополнительной информации. Шифрование и хеширование — два примера методов псевдонимизации. Псевдонимизированные данные, тем не менее, по-прежнему остаются связаны с исходными данными.
G.1.2. Пример анонимизации
Предположим, что необходимо замаскировать адреса электронной почты и телефонные номера:
SELECT * FROM people; id | firstname | lastname | phone ----+-----------+----------+------------ T1 | Sarah | Connor | 0609110911
Активируйте механизм динамического маскирования:
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.fake_last_name()'; SECURITY LABEL FOR anon ON COLUMN people.phone IS 'MASKED WITH FUNCTION anon.partial(phone,2,$$******$$,2)';
Подключитесь к серверу от имени недоверенного пользователя:
\connect - skynet SELECT * FROM people; id | firstname | lastname | phone ----+-----------+-----------+------------ T1 | Sarah | Stranahan | 06******11
G.1.3. Установка и подготовка
Расширение pgpro_anonymizer поставляется вместе с Postgres Pro Enterprise в виде отдельного пакета pgpro-anonymizer-ent-15 (подробные инструкции по установке приведены в Главе 17). Чтобы включить pgpro_anonymizer, выполните следующие действия:
Добавьте имя библиотеки в переменную
shared_preload_librariesв файлеpostgresql.conf:shared_preload_libraries = 'anon'
Перезагрузите сервер баз данных, чтобы изменения вступили в силу.
Примечание
Чтобы убедиться, что библиотека установлена правильно, вы можете выполнить следующую команду:
SHOW shared_preload_libraries;
Создайте расширение, выполнив следующий запрос:
CREATE EXTENSION anon CASCADE;
Инициализируйте расширение:
SELECT anon.init();
Функция
init()импортирует набор случайных данных по умолчанию (IBAN (международный номер банковского счёта), имена, города и т. д.). Это очень маленький набор данных на английском языке (1000 значений для каждой категории).
G.1.4. Конфигурирование
У расширения есть несколько параметров, которые можно задать на уровне всего экземпляра БД (в postgresql.conf или используя команду ALTER SYSTEM).
Их также можно (и зачастую лучше) задать на уровне базы данных следующим образом:
ALTER DATABASE customers SET anon.algorithm = sha512;
Следующие параметры может изменить только суперпользователь:
anon.algorithmМетод хеширования, используемый функциями псевдонимизации. Обратитесь к документации pgcrypto для ознакомления со списком доступных параметров.
См.
anon.salt, чтобы узнать, почему данный параметр является критическим для конфиденциальности данных.Тип:
textПо умолчанию:
sha256Видимость: только для суперпользователей
anon.maskschemaСхема (то есть пространство имён), в которой будут храниться представления динамического маскирования.
Тип:
textПо умолчанию:
maskВидимость: для всех пользователей
anon.restrict_to_trusted_schemasКогда этот параметр включён (по умолчанию), правила маскирования должны определяться, используя функции, находящиеся в ограниченном списке схем. По умолчанию только
anonсчитается доверенной схемой.Это повышает безопасность, не позволяя пользователям объявлять собственные фильтры маскирования.
Это также означает, что схема должна указываться явно в правилах маскирования. За подробной информацией обратитесь к разделу Создание собственных масок в главе Функции маскирования.
SECURITY LABEL FOR anon ON COLUMN people.name IS 'MASKED WITH FUNCTION lower(people.name)';
Правильный способ объявить эту функцию:
SECURITY LABEL FOR anon ON COLUMN people.name IS 'MASKED WITH FUNCTION pg_catalog.lower(people.name)';
Тип:
booleanПо умолчанию:
on(вкл.)Видимость: для всех пользователей
anon.saltСоль (salt), которая используется функциями псевдонимизации. Очень важно задать пользовательскую соль для каждой базы данных следующим образом:
ALTER DATABASE foo SET anon.salt = 'This_Is_A_Very_Secret_Salt';
Если недоверенный пользователь сможет прочитать соль, он сможет использовать атаку полным перебором, чтобы получить исходные данные на основе следующих элементов:
Псевдонимизированные данные
Алгоритм хеширования (см.
anon.algorithm)Значение соли
Соль и имя алгоритма хеширования должны быть защищены тем же уровнем безопасности, что и сами данные. Вот почему значение соли следует хранить непосредственно в базе данных, используя команду
ALTER DATABASE.Тип:
textПо умолчанию: (не задан)
Видимость: только для суперпользователей
anon.sourceshemaСхема (то есть пространство имён), в которой таблицы маскируются механизмом динамического маскирования.
Измените это значение перед запуском динамического маскирования.
ALTER DATABASE foo SET anon.sourceschema TO 'my_app';
Затем переподключитесь, чтобы изменение вступило в силу, и запустите модуль.
SELECT anon.start_dynamic_masking();
Тип:
textПо умолчанию:
publicВидимость: для всех пользователей
G.1.5. Объявление правил маскирования
Основная идея данного расширения — предложить встроенную анонимность.
Правила маскирования данных должны быть написаны разработчиками приложения, потому что они лучше всех знают, как работает модель данных. Поэтому правила маскирования должны реализовываться непосредственно внутри схемы базы данных.
Расширение позволяет маскировать данные непосредственно внутри экземпляра Postgres Pro без использования внешнего инструмента и таким образом ограничивает раскрытие информации и уменьшает риск утечки данных.
Правила маскирования данных задаются посредством меток безопасности:
CREATE TABLE player (id SERIAL, name TEXT, points INT); INSERT INTO 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';
Важно
Максимальная длина правила маскирования данных составляет 1024 знака. Если требуется правило большей длины, следует создать собственную функцию маскирования.
Важно
Правила маскирования не наследуются. Если вы разбили таблицу на несколько секций, необходимо объявить правила маскирования для каждой секции.
G.1.5.1. Экранирование строковых литералов (констант)
Как вы могли заметить, определения правил маскирования заключаются в одинарные кавычки. Поэтому, если в правиле маскирования нужно записать строку, экранируйте её символами доллара:
SECURITY LABEL FOR anon ON COLUMN player.name IS 'MASKED WITH VALUE $$CONFIDENTIAL$$';
G.1.5.2. Отображение правил маскирования
Для отображения всех правил маскирования, объявленных в текущей базе данных, используйте anon.pg_masking_rules:
SELECT * FROM anon.pg_masking_rules;
G.1.5.3. Отладка правил маскирования
Подробную информацию об ошибках в правилах маскирования возможно получить, задав для client_min_messages значение DEBUG.
postgres=# SET client_min_messages=DEBUG; postgres=# SELECT anon.anonymize_database(); DEBUG: Anonymize table public.bar with firstname = anon.fake_first_name() DEBUG: Anonymize table public.foo with id = NULL ERROR: Cannot mask a "NOT NULL" column with a NULL value HINT: If privacy_by_design is enabled, add a default value to the column CONTEXT: PL/pgSQL function anon.anonymize_table(regclass) line 47 at RAISE SQL function "anonymize_database" statement 1
G.1.5.4. Удаление правила маскирования
Вы легко можете удалить правило маскирования следующим образом:
SECURITY LABEL FOR anon ON COLUMN player.name IS NULL;
Чтобы удалить все правила сразу, используйте оператор:
SELECT anon.remove_masks_for_all_columns();
G.1.6. Функции маскирования
Данное расширение предоставляет функции для реализации следующих основных стратегий анонимизации:
В зависимости от ваших данных может потребоваться использовать разные стратегии для разных столбцов:
Для имён и других «прямых идентификаторов» лучше всего использовать фальсификацию.
Перестановка подходит для внешних ключей.
Добавление искажения лучше использовать для числовых значений и дат.
Частичное скрытие идеально подходит для адресов электронной почты и телефонных номеров.
G.1.6.1. Удаление
Самый быстрый и безопасный способ анонимизировать данные — удалить их.
Во многих случаях лучший способ скрыть содержимое столбца — заменить все значения одним статическим значением.
Например, вы можете заменить весь столбец словом CONFIDENTIAL, как указано ниже:
SECURITY LABEL FOR anon ON COLUMN users.address IS 'MASKED WITH VALUE $$CONFIDENTIAL$$';
G.1.6.2. Добавление искажения
Этот способ также называется Отклонение. В его основе лежит «сдвиг» дат и числовых значений. Например, после применения отклонения +/- 10% к столбцу зарплаты набор данных не потеряет смысл.
-
anon.noise(noise_valueanyelement,ratiodouble precision) Если параметр
ratio= 0.33, все значения столбца будут случайным образом смещены с коэффициентом +/- 33%.-
anon.dnoise(noise_valueanyelement,noise_rangeinterval) Если
интервал= «2 дня», возвращаемое значение будет исходным значением, сдвинутым случайным образом на +/- 2 дня.
Важно
Маскирующие функции noise() уязвимы для атак повторного воспроизведения, особенно при использовании динамического маскирования. Недоверенный пользователь может угадать исходное значение, несколько раз запросив замаскированное значение, а затем просто использовать функцию avg(), чтобы получить хорошее приближение. В целом, эти функции лучше всего подходят для выгрузки анонимизированных данных и статического маскирования, а при использовании динамического маскирования их следует избегать.
G.1.6.3. Рандомизация
Данное расширение предоставляет большой выбор функций для генерации абсолютно случайных данных:
G.1.6.3.1. Основные случайные значения
G.1.6.3.2. Рандомизация между указанными значениями
Чтобы выбрать любое значение между двумя границами:
Примечание
Возможный результат этих функций включает верхнюю и нижнюю границу значений. Например, функция anon.random_int_between(1,3) возвращает 1, 2 или 3.
За подробным описанием интервалов обратитесь к разделу Рандомизация в диапазоне.
G.1.6.3.3. Рандомизация в массиве
Функция random_in возвращает элемент указанного массива. Например:
-
anon.random_in(ARRAY[1,2,3]) Возвращает целое число между
1и3.-
anon.random_in(ARRAY['red','green','blue']) Возвращает случайное текстовое значение из массива ['red', 'green', 'blue'].
G.1.6.3.4. Рандомизация в перечислениях
Эта функция возвращает элемент из указанной переменной типа ENUM.
-
anon.random_in_enum(variable_of_an_enum_type) Возвращает случайное значение.
CREATE TYPE card AS ENUM ('visa', 'mastercard', ‘amex’);
SELECT anon.random_in_enum(NULL::CARD);
random_in_enum
----------------
mastercard
CREATE TABLE customer (
id INT,
...
credit_card CARD
);
SECURITY LABEL FOR anon ON COLUMN customer.creditcard
IS 'MASKED WITH FUNCTION anon.random_in_enum(creditcard)'G.1.6.3.5. Рандомизация в диапазоне
Типы RANGE — это мощный инструмент для описания интервала значений, который позволяет определять, включаются ли в диапазон соответствующие границы, за подробной информацией обратитесь к примерам.
Для каждого подтипа диапазона существует своя функция:
-
anon.random_in_int4range('[5,6)') Возвращает INT значения 5.
-
anon.random_in_int8range('(6,7]') Возвращает BIGINT значения 7.
-
anon.random_in_numrange('[0.1,0.9]') Возвращает значение NUMERIC между 0.1 и 0.9.
-
anon.random_in_daterange('[2001-01-01, 2001-12-31)') Возвращает дату в 2001 году.
-
anon.random_in_tsrange('[2022-10-01,2022-10-31]') Возвращает значение TIMESTAMP в октябре 2022 года.
-
anon.random_in_tstzrange('[2022-10-01,2022-10-31]') Возвращает значение TIMESTAMP WITH TIMEZONE в октябре 2022 года.
Примечание
Невозможно получить случайное значение типа RANGE с бесконечной границей. Например, anon.random_in_int4range('[2022,)') возвращает NULL.
G.1.6.4. Фальсификация
Идея фальсификации заключается в замене конфиденциальных данных случайными, но правдоподобными значениями. Цель состоит в том, чтобы избежать какой-либо идентификации по записи данных, оставляя её при этом пригодной для тестирования, анализа и обработки данных.
Предоставляются следующие функции фальсификации:
-
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() Возвращает случайный действительный почтовый индекс.
Для столбцов типа text и varchar вы можете использовать классический генератор Lorem Ipsum:
-
anon.lorem_ipsum() Возвращает пять абзацев.
-
anon.lorem_ipsum(2) Возвращает два абзаца.
-
anon.lorem_ipsum( paragraphs := 4 ) Возвращает четыре абзаца.
-
anon.lorem_ipsum( words := 20 ) Возвращает 20 слов.
-
anon.lorem_ipsum( characters := anon.length(table.column) ) Возвращает то же количество символов, что и в исходной строке.
G.1.6.5. Расширенная фальсификация
Генерация фальсифицированных данных — сложная тема. Представленные здесь функции ограничены базовым вариантом использования. Чтобы узнать о более продвинутых методах фальсификации, в частности, если вам нужны локализованные фальсифицированные данные, взгляните на PostgreSQL Faker, расширение, основанное на известной библиотеке Faker языка Python.
Данное расширение предоставляет расширенный механизм фальсификации с поддержкой локализации.
Например:
CREATE SCHEMA faker;
CREATE EXTENSION faker SCHEMA faker;
SELECT faker.faker('de_DE');
SELECT faker.first_name_female();
first_name_female
-------------------
MirjaG.1.6.6. Псевдонимизация
Псевдонимизация аналогична фальсификации в плане создания реалистичных значений. Основное отличие состоит в том, что псевдонимизация является детерминированной: функции всегда будут возвращать одно и то же фальсифицированное значение на основе значения затравки и необязательного значения соли.
Предоставляются следующие функции псевдонимизации:
-
anon.pseudo_first_name(seedanyelement,salttext) Возвращает случайное имя.
-
anon.pseudo_last_name(seedanyelement,salttext) Возвращает случайную фамилию.
-
anon.pseudo_email(seedanyelement,salttext) Возвращает действительный адрес электронной почты.
-
anon.pseudo_city(seedanyelement,salttext) Возвращает название существующего города.
-
anon.pseudo_country(seedanyelement,salttext) Возвращает название существующей страны.
-
anon.pseudo_company(seedanyelement,salttext) Возвращает случайное название компании.
-
anon.pseudo_iban(seedanyelement,salttext) Возвращает случайный действительный номер IBAN (международный номер банковского счёта).
-
anon.pseudo_siret(seedanyelement,salttext) Возвращает действительный код SIRET.
Второй аргумент (salt) является необязательным. Вы можете вызывать каждую функцию с затравкой вида anon.pseudo_city('bob'), без указания соли. Соль нужна, чтобы увеличить сложность и избежать атак по словарю и полным перебором. Если соль не указана, вместо неё используется значение параметра конфигурации anon.salt (за подробным описанием обратитесь к разделу Универсальное хеширование).
Затравка может представлять собой любую информацию, относящуюся к субъекту. Например, можно постоянно генерировать один и тот же фальсифицированный адрес электронной почты для конкретного человека, используя его логин в качестве затравки:
SECURITY LABEL FOR anon ON COLUMN users.emailaddress IS 'MASKED WITH FUNCTION anon.pseudo_email(users.login)';
Примечание
Вы можете создавать уникальные значения, используя функцию псевдонимизации. Например, если вы хотите замаскировать столбец email, объявленный как UNIQUE, вам нужно инициализировать расширение с фальсифицированным набором данных, который намного больше, чем количество строк в таблице. В противном случае вы можете столкнуться с «коллизиями», то есть ситуациями, когда два разных исходных значения создают одно и то же псевдозначение.
Важно
Псевдонимизацию часто путают с анонимизацией, но на самом деле они служат двум разным целям: псевдонимизация — это способ защитить личную информацию, но псевдонимизированные данные по-прежнему «связаны» с реальными данными.
G.1.6.7. Общее хеширование
Теоретически хеширование не является надёжным методом анонимизации, однако на практике иногда необходимо создать детерминированный хеш оригинальных данных.
Например когда пара первичный/внешний ключ является «естественным ключом», она может содержать актуальную информацию (номер клиента, содержащий дату рождения или что-то подобное).
Хеширование таких столбцов позволяет сохранить ссылочную целостность даже для относительно необычных исходных данных.
-
anon.digest(valuetext,salttext,algorithmtext) Позволяет выбрать соль и алгоритм хеширования. Поддерживаемые алгоритмы:
md5,sha1,sha224,sha256,sha384иsha512.-
anon.hash(valuetext) Возвращает текстовый хеш значения, используя секретную соль, заданную в параметре
anon.salt, и алгоритм хеширования, заданный в параметреanon.algorithm. Параметрanon.algorithmимеет следующие возможные значения:md5,sha1,sha224,sha256(по умолчанию),sha384илиsha512. Значением по умолчанию параметраanon.saltявляется пустая строка. Вы можете изменить эти значения:ALTER DATABASE foo SET anon.salt TO 'xsfnjefnjsnfjsnf'; ALTER DATABASE foo SET anon.algorithm TO 'sha384';
Имейте в виду, что хеширование — это форма псевдонимизации. Это означает, что данные могут быть «деанонимизированы» с помощью хешированного значения и функции маскирования. Если злоумышленники получат доступ к этим двум элементам, они смогут идентифицировать некоторых людей, используя методы атаки brute force (полным перебором) или dictionary (по словарю). Поэтому соль и алгоритм, используемые для хеширования данных, нужно защищать так же надёжно, как и исходный набор данных.
В двух словах, мы рекомендуем вам использовать функцию anon.hash(), а не anon.digest(), потому что тогда соль не будет отображаться в правиле маскирования открытым текстом.
Кроме того, на практике хеш-функция вернёт длинную строку символов, например:
SELECT anon.hash('bob');
hash
----------------------------------------------------------------------------------------------------------------------------------
95b6accef02c5a725a8c9abf19ab5575f99ca3d9997984181e4b3f81d96cbca4d0977d694ac490350e01d0d213639909987ef52de8e44d6258d536c55e427397Для некоторых столбцов эта строка может быть слишком длинной, и вам, возможно, придётся вырезать некоторые части хеша, чтобы она поместилась в столбец. Например, если у вас есть внешний ключ на основе номера телефона, а столбец представляет собой varchar(12), вы можете преобразовать данные следующим образом:
SECURITY LABEL FOR anon ON COLUMN people.phone_number IS 'MASKED WITH FUNCTION anon.left(anon.hash(phone_number),12)'; SECURITY LABEL FOR anon ON COLUMN call_history.fk_phone_number IS 'MASKED WITH FUNCTION anon.left(anon.hash(fk_phone_number),12)';
Конечно, сокращение хеш-значения до 12 символов повысит риск «коллизии» (два разных значения имеют один и тот же фальсифицированный хеш). Решение о принятии такого риска остаётся за вами.
G.1.6.8. Частичное скрытие
Частичное скрытие оставляет часть данных нетронутыми. Например: номер кредитной карты может быть заменён на «40XX XXXX XXXX XX96».
-
anon.partial(inputtext,prefixint,paddingtext,suffixint) Частично заменяет заданный текст. Например,
anon.partial('abcdefgh',1,'xxxx',3)возвращаетaxxxxfgh.-
anon.partial_email(emailtext) Частично заменяет указанный адрес электронной почты. Например,
anon.partial_email('daamien@gmail.com')возвращаетda******@gm******.com.
G.1.6.9. Условное маскирование
В некоторых случаях фильтр маскирования стоит применять только для определённого значения или для ограниченного количества строк в таблице.
Например, если требуется «сохранить значения NULL», то есть маскировать только те строки, которые содержат значения, можно использовать функцию anon.ternary, которая работает как выражение CASE WHEN x THEN y ELSE z:
SECURITY LABEL FOR anon ON COLUMN player.score
IS 'MASKED WITH FUNCTION anon.ternary(score IS NULL,
NULL,
anon.random_int_between(0,100));Возможно также потребуется исключить некоторые строки в таблице. Например, сохранить пароли некоторых пользователей, чтобы они по-прежнему могли подключаться к тестовой версии приложения:
SECURITY LABEL FOR anon ON COLUMN account.password IS 'MASKED WITH FUNCTION anon.ternary( id > 1000, NULL::TEXT, password)';
Предупреждение
Условное маскирование может создать частично детерминированную «связь» между исходными данными и маскированными данными. Эту связь можно использовать для восстановления персональных данных из маскированных данных. Например, если сохранять значения NULL в столбце «дата_смерти», по этим значениям можно определить, кто жив. Таким образом, условное маскирование зачастую может привести к формированию набора данных, который не является полностью анонимизированным и всё ещё технически считается содержащим персональные данные.
G.1.6.10. Обобщение
Обобщение — это принцип замены исходного значения диапазоном, содержащим это значение. Например, вместо «Полу 42 года», можно сказать «Полу от 40 до 50 лет».
Примечание
Функции обобщения выполняют преобразование типа данных. Поэтому их невозможно использовать с механизмом динамического маскирования. Однако они полезны для создания анонимизированных представлений. См. пример ниже.
Рассмотрим следующую таблицу, содержащую медицинские данные:
SELECT * FROM patient; id | name | zipcode | birth | disease ----+----------+----------+------------+--------------- 1 | Alice | 47678 | 1979-12-29 | Heart Disease 2 | Bob | 47678 | 1959-03-22 | Heart Disease 3 | Caroline | 47678 | 1988-07-22 | Heart Disease 4 | David | 47905 | 1997-03-04 | Flu 5 | Eleanor | 47909 | 1999-12-15 | Heart Disease 6 | Frank | 47906 | 1968-07-04 | Cancer 7 | Geri | 47605 | 1977-10-30 | Heart Disease 8 | Harry | 47673 | 1978-06-13 | Cancer 9 | Ingrid | 47607 | 1991-12-12 | Cancer
Мы можем построить представление на основании этой таблицы, в котором будут исключены некоторые столбцы (SSN и name) и обобщить почтовый индекс и дату рождения следующим образом:
CREATE VIEW anonymized_patient AS
SELECT
'REDACTED' AS lastname,
anon.generalize_int4range(zipcode,100) AS zipcode,
anon.generalize_tsrange(birth,'decade') AS birth,
disease
FROM patient;Анонимизированная таблица теперь выглядит так:
SELECT * FROM anonymized_patient; lastname | zipcode | birth | disease ----------+---------------+-----------------------------+--------------- REDACTED | [47600,47700) | ["1970-01-01","1980-01-01") | Heart Disease REDACTED | [47600,47700) | ["1950-01-01","1960-01-01") | Heart Disease REDACTED | [47600,47700) | ["1980-01-01","1990-01-01") | Heart Disease REDACTED | [47900,48000) | ["1990-01-01","2000-01-01") | Flu REDACTED | [47900,48000) | ["1990-01-01","2000-01-01") | Heart Disease REDACTED | [47900,48000) | ["1960-01-01","1970-01-01") | Cancer REDACTED | [47600,47700) | ["1970-01-01","1980-01-01") | Heart Disease REDACTED | [47600,47700) | ["1970-01-01","1980-01-01") | Cancer REDACTED | [47600,47700) | ["1990-01-01","2000-01-01") | Cancer
Обобщенные значения по-прежнему полезны для статистики, поскольку они остаются верными, но они менее точны и, следовательно, снижают риск идентификации.
Postgres Pro предлагает несколько диапазонных типов данных, которые идеально подходят для дат и числовых значений.
Для числовых значений доступны следующие функции:
-
generalize_int4range(value,step)
generalize_int8range(value,step)
generalize_numrange(value,step) где
value— данные, которые будут обобщены, аstep— размер каждого диапазона.
G.1.6.11. Использование функций pg_catalog
Схема pg_catalog не является доверенной по умолчанию. Эта мера безопасности принимается, чтобы предотвратить использование пользователями сложных функций в правилах маскирования (таких как pg_catalog.query_to_xml, pg_catalog.ts_stat или функций системного администрирования), которые не должны применяться в качестве функций маскирования.
Однако данное расширение предоставляет привязки к некоторым полезным и безопасным функциям из схемы pg_catalog:
anon.concat(TEXT,TEXT)anon.concat(TEXT,TEXT, TEXT)anon.date_add(TIMESTAMP WITH TIME ZONE,INTERVAL)anon.date_part(TEXT,TIMESTAMP)anon.date_part(TEXT,INTERVAL)anon.date_subtract(TIMESTAMP WITH TIME ZONE, INTERVAL )anon.date_trunc(TEXT,TIMESTAMP)anon.date_trunc(TEXT,TIMESTAMP WITH TIME ZONE,TEXT)anon.date_trunc(TEXT,INTERVAL)anon.left(TEXT,INTEGER)anon.length(TEXT)anon.lower(TEXT)anon.make_date(INT,INT,INT )anon.make_time(INT,INT,DOUBLE PRECISION)anon.md5(TEXT)anon.random()anon.replace(TEXT,TEXT,TEXT)anon.regexp_replace(TEXT,TEXT,TEXT)anon.regexp_replace(TEXT,TEXT,TEXT,TEXT)anon.right(TEXT,INTEGER)anon.substr(TEXT,INTEGER)anon.substr(TEXT,INTEGER,INTEGER)anon.upper(TEXT)
Если требуется больше связок, выполните одно из следующих действий:
Создайте собственную маску в доверенной схеме.
Установите схему
pg_catalogкакTRUSTED(не рекомендуется).
G.1.6.12. Создание собственных масок
Вы также можете использовать свою собственную функцию в качестве маски. Функция должна быть либо деструктивной (например, частичное скрытие), либо вносить некоторые случайные значения в набор данных (например, фальсификация).
Для сложных типов данных, возможно, придётся создать собственную функцию. Этим приёмом придётся часто пользоваться, если нужно скрыть определённые части поля JSON.
Например:
CREATE TABLE company ( business_name TEXT, info JSONB )
Поле info содержит неструктурированные данные, как показано ниже:
SELECT jsonb_pretty(info) FROM company WHERE business_name = 'Soylent Green';
jsonb_pretty
----------------------------------
{
"employees": [
{
"lastName": "Doe",
"firstName": "John"
},
{
"lastName": "Smith",
"firstName": "Anna"
},
{
"lastName": "Jones",
"firstName": "Peter"
}
]
}
(1 row)Используя функции и операторы JSON, вы можете просмотреть ключи и при необходимости заменить конфиденциальные значения.
CREATE SCHEMA custom_masks;
-- Для этого требуются права суперпользователя
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$;Затем проверьте правильность работы функции:
SELECT custom_masks.remove_last_name(info) FROM company;
Если всё в порядке, вы можете объявить эту функцию как маску поля info:
SECURITY LABEL FOR anon ON COLUMN company.info IS 'MASKED WITH FUNCTION custom_masks.remove_last_name(info)';
И использовать её:
SELECT anonymize_table('company');
SELECT jsonb_pretty(info) FROM company WHERE business_name = 'Soylent Green';
jsonb_pretty
-------------------------------------
{
"employees": [ +
{ +
"lastName": "Prawdzik",+
"firstName": "John" +
}, +
{ +
"lastName": "Baltazor",+
"firstName": "Anna" +
}, +
{ +
"lastName": "Taylan", +
"firstName": "Peter" +
} +
] +
}
(1 row)Это самый простой пример. Как видите, управлять сложной структурой JSON с помощью языка SQL возможно, но поначалу это может быть сложно. Существует несколько способов обхода ключей и обновления значений. Вам, вероятно, придётся попробовать разные подходы в зависимости от ваших реальных данных JSON и производительности, которую вы хотите достичь.
G.1.7. Статическое маскирование
Иногда полезно напрямую преобразовать исходный набор данных. Сделать это можно разными способами:
Все эти способы уничтожают исходные данные. Используйте их с осторожностью.
G.1.7.1. Применение правил маскирования
Вы можете навсегда применить правила маскирования базы данных, используя функцию anon.anonymize database().
Рассмотрим простой пример:
CREATE TABLE customer ( id SERIAL, full_name TEXT, birth DATE, employer TEXT, zipcode TEXT, fk_shop INTEGER ); INSERT INTO customer VALUES (911,'Chuck Norris','1940-03-10','Texas Rangers', '75001',12), (312,'David Hasselhoff','1952-07-17','Baywatch', '90001',423) ; SELECT * FROM customer; id | full_name | birth | employer | zipcode | fk_shop -----+------------------+------------+---------------+---------+--------- 911 | Chuck Norris | 1940-03-10 | Texas Rangers | 75001 | 12 112 | David Hasselhoff | 1952-07-17 | Baywatch | 90001 | 423
Объявите правила маскирования:
SECURITY LABEL FOR anon ON COLUMN customer.full_name IS 'MASKED WITH FUNCTION anon.fake_first_name() || '' '' || anon.fake_last_name()'; SECURITY LABEL FOR anon ON COLUMN customer.employer IS 'MASKED WITH FUNCTION anon.fake_company()'; SECURITY LABEL FOR anon ON COLUMN customer.zipcode IS 'MASKED WITH FUNCTION anon.random_zip()';
Замените исходные данные в замаскированных столбцах:
SELECT anon.anonymize_database(); SELECT * FROM customer; id | full_name | birth | employer | zipcode | fk_shop -----+-------------+------------+---------------------+---------+--------- 911 | Jesse Kosel | 1940-03-10 | Marigold Properties | 62172 | 12 312 | Leolin Bose | 1952-07-17 | Inventure | 20026 | 423
Вы также можете использовать функции anonymize_table() и anonymize_column() для удаления данных из подмножества строк базы данных:
SELECT anon.anonymize_table('customer');
SELECT anon.anonymize_column('customer','zipcode');Важно
Статическое маскирование — медленный процесс. Принцип статического маскирования заключается в обновлении всех строк всех таблиц, содержащих хотя бы один маскируемый столбец. В целом это означает, что сервер перезапишет все данные на диске. В зависимости от размера базы данных, аппаратного обеспечения и конфигурации экземпляра может оказаться быстрее экспортировать анонимизированные данные (см. Выгрузка анонимизированных данных) и повторно загрузить их в базу данных.
G.1.7.2. Перестановка
Перестановка перемешивает значения в пределах столбца.
-
anon.shuffle_column(shuffle_tableregclass,shuffle_columnname,primary_keyname) Переставляет все значения в заданном столбце. Вам необходимо указать первичный ключ таблицы.
Этот способ полезен для внешних ключей, поскольку будет сохранена ссылочная целостность.
Важно
shuffle_column() не является маскирующей функцией, потому что она работает «вертикально»: она изменит все значения столбца сразу.
G.1.7.3. Добавление искажения в столбец
Есть также некоторые функции, которые могут добавить искажение во все значения столбца:
-
anon.add_noise_on_numeric_column(tableregclass,columntext,ratiofloat) Если параметр
ratio= 0.33, все значения столбца будут случайным образом смещены с коэффициентом +/- 33%.-
anon.add_noise_on_datetime_column(tableregclass,columntext,intervalinterval) Если параметр
interval=2 дня, все значения столбца будут случайным образом сдвинуты на +/- 2 дня.
Важно
Эти функции искажения уязвимы для атак повторного воспроизведения.
G.1.8. Динамическое маскирование
Вы можете скрыть некоторые данные от роли, объявив эту роль как «MASKED». Другие роли по-прежнему будут иметь доступ к исходным данным.
CREATE TABLE people (id TEXT, firstname TEXT, lastname TEXT, phone TEXT);
INSERT INTO people VALUES ('T1','Sarah', 'Connor','0609110911');
SELECT * FROM people;
SELECT * FROM people;
id | firstname | lastname | phone
----+----------+----------+------------
T1 | Sarah | Connor | 0609110911
(1 row)Активируйте механизм динамического маскирования:
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.fake_last_name()'; SECURITY LABEL FOR anon ON COLUMN people.phone IS 'MASKED WITH FUNCTION anon.partial(phone,2,$$******$$,2)';
Подключитесь к серверу от имени недоверенного пользователя:
\c - skynet SELECT * FROM people; id | firstname | lastname | phone ----+----------+-----------+------------ T1 | Sarah | Stranahan | 06******11 (1 row)
G.1.8.1. Изменение типа замаскированного столбца
Когда активировано динамическое маскирование, запрещено изменять тип данных столбца, если на нём есть маска.
Чтобы изменить замаскированный столбец, вам нужно временно отключить механизм маскирования следующим образом:
BEGIN; SELECT anon.stop_dynamic_masking(); ALTER TABLE people ALTER COLUMN phone TYPE VARCHAR(255); SELECT anon.start_dynamic_masking(); COMMIT;
G.1.8.2. Удаление замаскированной таблицы
Механизм динамического маскирования будет строить маскирующие представления для замаскированных таблицах. Это означает, что невозможно напрямую удалить замаскированную таблицу. Вы получите такую ошибку:
DROP TABLE people; psql: ERROR: cannot drop table people because other objects depend on it DETAIL: view mask.company depends on table people
Для эффективного удаления таблицы необходимо добавить параметр CASCADE, чтобы маскирующее представление тоже удалялось:
DROP TABLE people CASCADE;
G.1.8.3. Снятие маски с роли
Просто удалить защитную метку можно следующим образом:
SECURITY LABEL FOR anon ON ROLE bob IS NULL;
Сделать сразу все недоверенные роли обычными можно так:
SELECT anon.remove_masks_for_all_roles();
G.1.8.4. Ограничения
G.1.8.4.1. Отображение списка таблиц
Из-за особенностей работы механизма динамического маскирования, если недоверенная роль попытается отобразить таблицы в psql с помощью команды \dt, psql не покажет никаких таблиц.
Это связано с тем, что расширение подменяет search_path для недоверенных ролей.
Вы можете попробовать явно добавить схему, которую хотите найти, например:
\dt *.* \dt public.*
G.1.8.4.2. Поддержка только одной схемы
Система динамического маскирования работает только с одной схемой (по умолчанию — public). Когда вы запускаете механизм маскирования с помощью start_dynamic_masking(), вы можете указать схему, которая будет маскироваться, следующим образом:
ALTER DATABASE foo SET anon.sourceschema TO 'sales';
Затем откройте новый сеанс с базой данных и введите:
SELECT start_dynamic_masking();
Однако статическое маскирование функциями anon.anonymize_column(), anon.anonymize_table() и anon.anonymize_database() может работать с несколькими схемами.
G.1.8.4.3. Производительность
Известно, что динамическое маскирование работает очень медленно с некоторыми запросами, особенно при попытке соединить две таблицы по замаскированному ключу, используя хеширование или псевдонимизацию.
G.1.8.4.4. Графические инструменты
Когда вы используете недоверенную роль с графическим интерфейсом, таким как DBeaver или pgAdmin, панель data может выдавать следующую ошибку при попытке отобразить содержимое замаскированной таблицы с именем foo:
SQL Error [42501]: ERROR: permission denied for table foo
Это связано с тем, что большинство этих инструментов будут напрямую запрашивать таблицу public.foo вместо того, чтобы «перенаправлять» механизм маскирования на представление mask.foo.
Чтобы просмотреть замаскированные данные с помощью графического инструмента, вы можете:
Открыть панель запросов SQL и ввести SELECT * FROM foo
Перейти к Database > Schemas > mask > Views > foo
G.1.9. Выгрузка анонимизированных данных
G.1.9.1. pg_dump_anon.sh
Скрипт pg_dump_anon.sh поддерживает большинство параметров обычной команды pg_dump. Также поддерживаются переменные среды (PGHOST, PGUSER и т. д.) и файлы .pgpass.
G.1.9.2. Пример
Пользователь с именем bob может экспортировать анонимный архив базы данных app следующим образом:
/opt/pgpro/ent-15/bin/pg_dump_anon.sh -h localhost -U bob --password --file=anonymous_dump.sql app
Важно
Имя базы данных должно быть последним параметром.
Чтобы получить дополнительные сведения о поддерживаемых параметрах, просто введите ./pg_dump_anon.sh --help.
G.1.9.3. Ограничения
Пароль пользователя запрашивается автоматически. Это означает, что вы должны либо добавить параметр
--password, чтобы задавать его интерактивно, либо объявить его в переменнойPGPASSWORD, либо поместить его в файл.pgpass(однако в Windows переменнаяPGPASSFILEдолжна быть указана явно)Единственным поддерживаемым форматом является
plain. Другие форматы (custom,dirиtar) не поддерживаются.
G.1.10. Обобщение
G.1.10.1. Снижение точности конфиденциальных данных
Идея обобщения состоит в том, чтобы заменить данные более широкими и менее точными значениями. Например, вместо «Бобу 28 лет» можно сказать «Бобу от 20 до 30 лет». Это полезно для аналитики, потому что данные остаются верными, избегая при этом риска идентификации.
Обобщение — это способ достижения k-анонимности.
Postgres Pro может легко использовать обобщение благодаря диапазонным типам данных, которые являются очень эффективным способом хранения и работы с набором значений, содержащихся между нижней и верхней границей.
G.1.10.2. Пример
Рассмотрим таблицу с медицинскими данными:
SELECT * FROM patient;
ssn | firstname | zipcode | birth | disease
-------------+-----------+---------+------------+---------------
253-51-6170 | Alice | 47012 | 1989-12-29 | Heart Disease
091-20-0543 | Bob | 42678 | 1979-03-22 | Allergy
565-94-1926 | Caroline | 42678 | 1971-07-22 | Heart Disease
510-56-7882 | Eleanor | 47909 | 1989-12-15 | Acne
098-24-5548 | David | 47905 | 1997-03-04 | Flu
118-49-5228 | Jean | 47511 | 1993-09-14 | Flu
263-50-7396 | Tim | 47900 | 1981-02-25 | Heart Disease
109-99-6362 | Bernard | 47168 | 1992-01-03 | Asthma
287-17-2794 | Sophie | 42020 | 1972-07-14 | Asthma
409-28-2014 | Arnold | 47000 | 1999-11-20 | Diabetes
(10 rows)Мы хотим, чтобы анонимизированные данные оставались достоверными, потому что они будут использоваться для статистики. Можно построить представление этой таблицы, чтобы удалить бесполезные столбцы и обобщить косвенные идентификаторы:
CREATE MATERIALIZED VIEW generalized_patient AS SELECT 'REDACTED'::TEXT AS firstname, anon.generalize_int4range(zipcode,1000) AS zipcode, anon.generalize_daterange(birth,'decade') AS birth, disease FROM patient;
Это даст нам менее точное представление данных:
SELECT * FROM generalized_patient; firstname | zipcode | birth | disease -----------+---------------+-------------------------+--------------- REDACTED | [47000,48000) | [1980-01-01,1990-01-01) | Heart Disease REDACTED | [42000,43000) | [1970-01-01,1980-01-01) | Allergy REDACTED | [42000,43000) | [1970-01-01,1980-01-01) | Heart Disease REDACTED | [47000,48000) | [1980-01-01,1990-01-01) | Acne REDACTED | [47000,48000) | [1990-01-01,2000-01-01) | Flu REDACTED | [47000,48000) | [1990-01-01,2000-01-01) | Flu REDACTED | [47000,48000) | [1980-01-01,1990-01-01) | Heart Disease REDACTED | [47000,48000) | [1990-01-01,2000-01-01) | Asthma REDACTED | [42000,43000) | [1970-01-01,1980-01-01) | Asthma REDACTED | [47000,48000) | [1990-01-01,2000-01-01) | Diabetes (10 rows)
G.1.10.3. Функции обобщения
Расширение pgpro_anonymizer предоставляет функции обобщения для всех встроенных диапазонных типов. Как правило, эти функции принимают маскируемое значение в качестве первого параметра, а второй параметр является длиной шага.
Для числовых значений:
-
anon.generalize_int4range(valueinteger,stepinteger) Например,
anon.generalize_int4range(42,5)возвращает диапазон[40,45).-
anon.generalize_int8range(valueinteger,stepinteger) Например,
anon.generalize_int8range(12345,1000)возвращает диапазон[12000,13000).-
anon.generalize_numrange(valueinteger,stepinteger) Например,
anon.generalize_numrange(42.32378,10)возвращает диапазон[40,50).
Для значений времени:
-
anon.generalize_tsrange(valueinteger,stepinteger) Например,
anon.generalize_tsrange('1904-11-07','year')возвращает['1904-01-01','1905-01-01').-
anon.generalize_tstzrange(valueinteger,stepinteger) Например,
anon.generalize_tstzrange('1904-11-07','week')возвращает['1904-11-07','1904-11-14').-
anon.generalize_daterange(valueinteger,stepinteger) Например,
anon.generalize_daterange('1904-11-07','decade')возвращает[1900-01-01,1910-01-01).
Возможные значения шагов: микросекунда, миллисекунда, секунда, минута, час, день, неделя, месяц, год, десятилетие, столетие и тысячелетие.
G.1.10.4. Ограничения
G.1.10.4.1. Выделение и крайние значения
Выделение — это возможность изолировать человека в наборе данных на основании экстремальных или исключительных значений.
Например:
SELECT * FROM employees; id | name | job | salary ------+----------------+------+-------- 1578 | xkjefus3sfzd | NULL | 1498 2552 | cksnd2se5dfa | NULL | 2257 5301 | fnefckndc2xn | NULL | 45489 7114 | npodn5ltyp3d | NULL | 1821
В этой таблице мы видим, что у конкретного сотрудника очень высокая зарплата, гораздо выше средней. Следовательно, этот человек, вероятно, является генеральным директором компании.
При обобщении это важно, поскольку размер диапазона («шаг») должен быть достаточно широким, чтобы предотвратить идентификацию отдельного человека.
Достичь этого можно посредством реализации принципа k-анонимности.
G.1.10.4.2. Обобщение несовместимо с динамическим маскированием
По определению, при обобщении данные остаются верными, но изменяется тип столбца.
Это означает, что преобразование не является прозрачным, и поэтому его нельзя использовать с динамическим маскированием.
G.1.10.5. k-анонимность
k-анонимность — это стандартный отраслевой термин, используемый для описания свойства анонимизированного набора данных. Принцип k-анонимности гласит, что в заданном наборе данных любого анонимизированного человека нельзя отличить как минимум от k-1 других людей. Другими словами, k-анонимность можно описать как гарантию «укрытия в толпе». Низкое значение k увеличивает риск идентификации на основании связи с другими источниками данных.
-
anon.k_anonymity(idregclass) Вы можете оценить k-фактор анонимности таблицы следующим образом:
Определите столбцы, которые являются косвенными идентификаторами (также известными как квазиидентификаторы), например:
SECURITY LABEL FOR k_anonymity ON COLUMN patient.firstname IS 'INDIRECT IDENTIFIER'; SECURITY LABEL FOR k_anonymity ON COLUMN patient.zipcode IS 'INDIRECT IDENTIFIER'; SECURITY LABEL FOR k_anonymity ON COLUMN patient.birth IS 'INDIRECT IDENTIFIER';
После определения косвенных идентификаторов:
SELECT anon.k_anonymity('generalized_patient')Чем выше значение, тем лучше.
G.1.11. Производительность
Любой процесс анонимизации имеет свою цену, состоящую из процессорного времени, места в оперативной памяти и, возможно, большого количества дисковых операций ввода-вывода. Ниже приведён краткий обзор в зависимости от того, какую стратегию вы используете.
В двух словах, эффективность анонимизации в основном будет зависеть от следующих важных факторов:
Размера базы данных
Количества правил маскирования
G.1.11.1. Статическое маскирование
По сути, статическое маскирование полностью перезаписывает замаскированные таблицы на диске. Это может занимать много времени в зависимости от вашей среды. И во время этого процесса таблицы будут заблокированы.
Примечание
В этом случае стоимость анонимизации «оплачивается» всеми пользователями, но оплачивается раз и навсегда.
G.1.11.2. Динамическое маскирование
При динамическом маскировании реальные данные заменяются «на лету» каждый раз, когда недоверенный пользователь отправляет запрос в базу данных. Это означает, что у недоверенных пользователей будет более медленный отклик, чем у обычных (доверенных). Обычно это нормально, потому что недоверенные пользователи как правило считаются менее важными, чем обычные.
Если вы примените к таблице три или четыре правила, время отклика для недоверенных пользователей должно быть примерно на 20-30% больше, чем для обычных.
Поскольку правила маскирования применяются для каждого запроса со стороны недоверенных пользователей, динамическое маскирование уместно, когда у вас есть ограниченное количество недоверенных пользователей, которые подключаются к базе данных только время от времени. Например, аналитик данных подключается раз в неделю для создания бизнес-отчёта.
Если есть несколько недоверенных пользователей или если недоверенный пользователь очень активен, рекомендуется один раз в неделю экспортировать замаскированные данные на дополнительный экземпляр и позволить этим пользователям подключаться к этому дополнительному экземпляру.
Примечание
В этом случае стоимость анонимизации «оплачивается» только недоверенными пользователями.
G.1.11.3. Выгрузка анонимизированных данных
Если процесс резервного копирования вашей базы данных занимает один час с использованием pg_dump, то анонимизация и экспорт всей базы данных с использованием pg_dump_anon.sh ориентировочно займёт два часа.
Примечание
В этом случае стоимость анонимизации «оплачивает» пользователь, запрашивающий анонимизированный экспорт. Другие пользователи базы данных не будут затронуты.
G.1.11.4. Ускорение процесса
G.1.11.4.1. Используйте MASKED WITH VALUE, когда это возможно
Заменить исходные данные статическим значением всегда быстрее, чем вызывать функции маскирования.
G.1.11.4.2. Извлечение выборки
Если требуется анонимизировать данные для целей тестирования, скорее всего, будет достаточно меньшего подмножества базы данных. В таком случае возможно ускорить анонимизацию, уменьшив объём данных.
За подробной информацией обратитесь к разделу Извлечение выборки.
G.1.11.4.3. Материализованные представления
Динамическое маскирование требуется не всегда. В некоторых случаях более эффективно создавать материализованные представления.
Например:
CREATE MATERIALIZED VIEW masked_customer AS
SELECT
id,
anon.random_last_name() AS name,
anon.random_date_between('1920-01-01'::DATE,now()) AS birth,
fk_last_order,
store_id
FROM customer;G.1.12. Безопасность
G.1.12.1. Разрешения
Ниже приведён обзор возможных действий пользователей в зависимости от имеющихся у них прав:
Таблица G.1. Права
| Действие | Суперпользователь | Владелец | Недоверенная роль |
|---|---|---|---|
| Создать расширение | Да | ||
| Удалить расширение | Да | ||
| Инициализировать расширение | Да | ||
| Сбросить параметры расширения | Да | ||
| Конфигурировать расширение | Да | ||
| Сделать роль недоверенной | Да | ||
| Начать динамическое маскирование | Да | ||
| Остановить динамическое маскирование | Да | ||
| Создать таблицу | Да | Да | |
| Объявить правило маскирования | Да | Да | |
| Добавить, удалить, изменить строку | Да | Да | |
| Использовать статическое маскирование | Да | Да | |
| Получить реальные данные | Да | Да | |
| Выгружать данные без анонимизации | Да | Да | |
| Выгружать анонимизированные данные | Да | Да | |
| Использовать маскирующие функции | Да | Да | Да |
| Получить замаскированные данные | Да | Да | Да |
| Посмотреть правила маскирования | Да | Да | Да |
G.1.12.2. Ограничение маскирующих фильтров доверенными схемами
По умолчанию владельцы баз данных смогут создавать правила маскирования только с функциями, расположенными в доверенных схемах, которые контролируются суперпользователями.
По умолчанию доверенной объявлена только схема anon. Это означает, что функции из pg_catalog по умолчанию нельзя использовать в правилах маскирования.
За подробной информацией обратитесь к разделу Использование функций pg_catalog.
G.1.12.3. Контекст безопасности функций
Большинство функций данного расширения объявлены с тегом SECURITY INVOKER. Это означает, что эти функции выполняются с правами пользователя, который их вызывает. Это важное ограничение.
Данное расширение содержит ещё несколько функций, объявленных с использованием тега SECURITY DEFINER.
G.1.13. Извлечение выборки
При разработке политики анонимизации для набора данных, скорее всего, не требуется анонимизировать всю базу данных. В большинстве случаев достаточно извлечь лишь подмножество таблицы. Например, если требуется экспортировать анонимные дампы данных для тестирования рабочего процесса CI, возможно, достаточно будет извлечь и замаскировать лишь 10% базы данных.
Кроме того, анонимизация меньшего фрагмента набора данных (то есть «выборки») выполняется значительно быстрее.
G.1.13.1. Пример
Допустим, есть огромный объём HTTP‑журналов, хранящихся в таблице. Вы хотите удалить IP‑адреса и извлечь лишь 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)';
Теперь можно выполнить статическое маскирование, динамическое маскирование или создать анонимные дампы. Маскированные данные будут представлять собой 10% от объёма реальных данных.
G.1.13.2. Синтаксис
Синтаксис полностью совпадает с синтаксисом предложения TABLESAMPLE, которое можно добавлять в конце оператора SELECT.
Также можно задать коэффициент выборки на уровне базы данных. Этот коэффициент будет применяться ко всем таблицам, для которых не определено собственное правило TABLESAMPLE.
SECURITY LABEL FOR anon ON DATABASE app IS 'TABLESAMPLE SYSTEM(33)';
G.1.13.3. Поддержание ссылочной целостности
Предупреждение
Метод извлечения выборки, описанный выше, завершится ошибкой при наличии внешних ключей, ссылающихся на таблицу, из которой вы хотите получить выборку.
Извлечение подмножества базы данных с сохранением ссылочной целостности является непростой задачей, и данное расширение его не поддерживает.
Если требуется сохранить ссылочную целостность в анонимизированном наборе данных, выполните следующие шаги:
Извлеките выборку, используя pg_sample.
Анонимизируйте полученную выборку.
Существуют и другие инструменты для извлечения выборки в Postgres Pro, но pg_sample — один из лучших.