Наши клиенты часто интересуются функцией маскировки данных, но зачастую им достаточно простого решения, которое они способны реализовать самостоятельно.

В этой статье кратко описывается механизм создания представления (VIEW) для отображения деперсонализованных данных.

В основе этой реализации лежит решение из статьи про получение случайной строки из таблицы Основной задачей маскировки данных является сокрытие реальных данных и невозможность их восстановления. При этом недостаточно просто скрыть конфиденциальные данные — нужно заменить их похожими.

Это связано с тем, что маскировка данных применяется обычно при тестировании приложений и данные должны быть максимально приближенными к реальным. Один из вариантов — это использование тех же самых данных в таблице, но взятых из других строк.

Итак, начнём.

В качестве примера используется таблица connections, в которой, кроме всего остального, существуют колонки ID и Client_port, которые нужно замаскировать. При этом колонка ID является первичным ключом.

Так как некоторые строки могут быть удалены и ID не является строго последовательным значением, создадим таблицу, в которой данные в таблице будут связаны с номером строки. Дело в том, что в PostgreSQL нет более быстрого способа выбрать данные по номеру строки. В Oracle можно было бы пропустить этот шаг.

create table client_port_ids
(
rowid serial PRIMARY KEY,
id integer
);
-- fill table with exists id numbers. table should be fill before masking
INSERT INTO client_port_ids (id ) SELECT id FROM connections ORDER BY id;

Так как необходимо, чтобы при каждом SELECT не выдавались разные значения в маскируемой строке, поэтому необходимо создать таблицу, в которой будет сохраняться связь между реальными данными и подставными.

create table client_port_map
(
src integer PRIMARY KEY,
dst integer
);

Создадим функцию маскировки для проверки того, не выдавались ли до этого значения маскированные данные, и в случае отсутствия этих данных, данные берутся из случайной строки.

CREATE OR REPLACE FUNCTION public.hide_client_port(
val integer)
RETURNS integer AS
$BODY$
DECLARE
res integer;
sed float;
row_count integer;
rand_row integer;
BEGIN
--check exist mapping
SELECT dst into res FROM client_port_map WHERE src = val;
IF FOUND = FALSE THEN
--search random string
select MAX(rowid) into row_count from client_port_ids;
LOOP
SELECT floor(random()*row_count) into rand_row;
select client_port into res from connections where id = (select id from client_port_ids where rowid = rand_row);
EXIT WHEN FOUND = TRUE;
END LOOP;
--save new value to mapping
INSERT INTO client_port_map VALUES (val, res);
END IF;
return res ;
END;
$BODY$
LANGUAGE plpgsql VOLATILE

Взглянем, как перемешиваются записи.

shuffling

Так как в данном примере используется небольшая таблица, то встречаются совпадения с реальными данными. Теорию вероятности не обманешь.

Какое применение можно найти этому? Создаём новую схему в котором создаём VIEW на таблице с реальными данными. А для таблицы connection мы создаём VIEW в таком виде:

CREATE OR REPLACE VIEW public.connection AS SELECT connections.partition_id,
connections.id,
connections.interface_id,
connections.client_host,
hide_client_port(connections.client_port) AS hide_client_port,
connections.begin_time,
connections.end_time,
connections.client_host_name,
connections.instance_id,
connections.proxy_id,
connections.sniffer_id
FROM connections;

где вставляем функцию, скрывающую данные.

Данную функцию можно улучшить, реализовав механизм. при котором одинаковым значениями из разных строк будут присваивается разные значения для сокрытия.

Комментарии