Разработчику нужно воспроизвести баг, который виден только на реальных данных. Первое желание — скинуть дамп производственной базы. Проблема: это нарушение закона о персональных данных (GDPR в Европе, 152-ФЗ в России). Здесь разбираем, как дать разработчику рабочие данные, не нарушая закон.
Почему нельзя просто передать дамп как есть
Каждая копия производственных данных — новая поверхность для утечки. Если дамп попадёт не туда (ноутбук, незашифрованный диск, мессенджер), вы получите юридическую ответственность, а пользователи — нарушение их приватности. Они не давали согласия на то, чтобы их данные хранились на рабочем ноутбуке стажёра.
Хорошая новость: почти все задачи разработки решаются синтетическими (сгенерированными) данными. Реальный дамп нужен редко — только когда воспроизводится конкретная проблема с конкретным набором строк.
Что такое PII
PII (Personally Identifiable Information) — данные, по которым можно идентифицировать человека.
Прямые идентификаторы — однозначно указывают на конкретного человека:
- имя, фамилия, отчество;
- email и телефон;
- адрес, паспорт, СНИЛС, ИНН;
- дата рождения;
- IP-адрес;
- номер банковской карты или счёта.
Косвенные идентификаторы — по отдельности безопасны, но вместе позволяют вычислить человека:
- точная геолокация;
- комбинация «профессия + город + год рождения» (в маленьком городе такая тройка уже уникальна);
- User-Agent браузера и fingerprint-поля.
Бизнес-данные — не PII, но тоже чувствительны:
- B2B-цены и скидки конкретных клиентов;
- внутренние комментарии модераторов;
- логи действий сотрудников.
Шесть стратегий анонимизации
Удаление — поставить NULL. Используйте, когда поле вообще не нужно для отладки.
UPDATE customer SET address = NULL, apartment = NULL;
Замена константой — все строки получают одно и то же значение. Подходит, когда нужна структура, но не содержимое.
UPDATE customer SET email = 'masked@example.com';
Псевдонимизация через хеш — каждый уникальный email превращается в уникальный псевдоним. Связи между строками сохраняются (тот же email → тот же псевдоним), восстановить оригинал без ключа нельзя.
UPDATE customer
SET email = 'user' || substring(md5(email), 1, 8) || '@example.test'
WHERE email IS NOT NULL;
Замена случайными данными (faker) — вместо реального имени подставляется случайное, но реалистичное. Набор выглядит естественно.
Перестановка (shuffle) — значения переставляются между строками. Распределение сохраняется, привязка к конкретному человеку теряется.
Обобщение (generalization) — точное значение заменяется менее точным: дата рождения → год, город → регион. Используется для аналитических данных, где важно распределение.
Как замаскировать конкретные поля
Псевдонимизация: одинаковый email всегда даст одинаковый псевдоним, поэтому уникальные ограничения и внешние ключи не ломаются.
UPDATE customer
SET email = 'user' || substring(md5(email), 1, 8) || '@example.test'
WHERE email IS NOT NULL;
Телефон
Случайный номер в формате российского мобильного:
UPDATE customer
SET phone = '+7000' || lpad((random() * 10000000)::int::text, 7, '0')
WHERE phone IS NOT NULL;
Имя и фамилия
Самый простой вариант — добавить суффикс из ID (уникальность гарантирована):
UPDATE customer SET
first_name = 'Имя' || id,
last_name = 'Фамилия' || id;
Если нужны реалистичные имена, используйте postgresql_anonymizer (подробнее ниже) или библиотеки генерации тестовых данных на стороне приложения: JavaFaker (Java), faker-js (Node/TypeScript), Faker (Python), gofakeit (Go).
Дата рождения
Убрать конкретный день, оставить год — возраст сохраняется приближённо:
UPDATE customer SET born_on = date_trunc('year', born_on)::date;
Данные карты
Обнулить всё, кроме технических полей:
UPDATE payment SET
card_last4 = '0000',
card_holder_name = 'TEST';
Текстовые поля (комментарии, описания)
Свободный текст — сложный случай. Пользователь мог написать в комментарии свой телефон или адрес. Надёжнее всего — обнулить или заменить заглушкой:
UPDATE order_comment SET body = '[REDACTED]' WHERE body IS NOT NULL;
Если содержимое важно для отладки — используйте NER (распознавание именованных сущностей) через внешний инструмент или функцию anon.partial_text() из postgresql_anonymizer.
Полный сценарий: от производственной базы до дампа
Никогда не анонимизируйте данные прямо в производственной базе. Алгоритм:
# 1. Скопировать данные в отдельную базу-анонимизатор
pg_dump prod | psql anonymizer
-- 2. Анонимизировать всё в одной транзакции
BEGIN;
UPDATE customer SET
email = 'user' || substring(md5(email), 1, 8) || '@example.test',
phone = '+7000' || lpad((random()*10000000)::int::text, 7, '0'),
first_name = 'Имя' || id,
last_name = 'Фамилия' || id;
UPDATE customer_address SET
street = NULL,
building = NULL,
apartment = NULL;
UPDATE customer_document SET
number = '0000' || lpad(id::text, 6, '0'),
issued_by = 'TEST';
UPDATE payment SET
card_last4 = '0000',
card_holder_name = 'TEST';
DELETE FROM audit_log WHERE created_at < now() - interval '7 days';
COMMIT;
# 3. Сделать дамп анонимизированной базы
pg_dump anonymizer -Fc > prod-anon-$(date +%F).dump
# 4. Передать разработчику
# 5. Удалить базу-анонимизатор — не оставляйте полуобработанные данные
dropdb anonymizer
Важно: скрипт анонимизации хранится в репозитории и проходит код-ревью. Не пишите его каждый раз заново по памяти — это источник ошибок.
postgresql_anonymizer
postgresql_anonymizer — расширение PostgreSQL, которое добавляет встроенные функции для генерации реалистичных данных и декларативные правила маскирования.
CREATE EXTENSION anon CASCADE;
SELECT anon.init();
-- Объявить правила маскирования
SECURITY LABEL FOR anon ON COLUMN customer.email
IS 'MASKED WITH FUNCTION anon.fake_email()';
SECURITY LABEL FOR anon ON COLUMN customer.last_name
IS 'MASKED WITH FUNCTION anon.fake_last_name()';
-- Сделать маскированный дамп
SELECT anon.dump('customer');
Расширение умеет и в динамическое маскирование: определённые роли видят только маскированные данные даже в живой базе. Это полезно, если нужно дать разработчику прямой доступ к базе без лишних сложностей с дампами.
Альтернативы: Greenmask, ARX, собственные скрипты.
Re-identification: когда анонимизации имени недостаточно
Классическая ошибка: заменить имя и email, но оставить точную дату рождения, профессию и город. В небольшом городе комбинация «врач, 1985 год, Кострома» может быть уникальной — человека можно найти без имени.
Этот риск называется re-identification — повторная идентификация через комбинацию косвенных признаков.
Как защититься:
- обобщайте поля-идентификаторы (дата рождения → год, город → регион);
- удаляйте или объединяйте редкие категории;
- для аналитических данных применяйте k-anonymity: каждая строка должна быть неотличима от не менее чем k других строк по всем идентифицирующим полям.
На практике для большинства задач разработки достаточно обобщения полей — k-anonymity нужна при передаче данных для аналитики.
Частые ошибки
Передать дамп без анонимизации «только надёжному человеку» — закон не делает исключений для надёжности. Каждая копия данных требует правового основания (согласие или DPA).
Замаскировать только имя, забыть про email или телефон — анонимизация работает только если охватить весь PII, а не часть.
Оставить базу-анонимизатор после дампа — полуобработанные данные остаются поверхностью для утечки. Удалите сразу.
Оставить свободный текст (комментарии) без обработки — там могут быть телефоны и имена, написанные пользователями.
Анонимизировать прямо в производственной базе — любая ошибка в скрипте необратимо изменит данные.
Коротко
- Передача производственного дампа без анонимизации нарушает закон.
- Для большинства задач разработки достаточно синтетических (сгенерированных) данных.
- PII: email, телефон, ФИО, адрес, документы, дата рождения, IP, карта; косвенные: геолокация, уникальные комбинации.
- Шесть стратегий: удаление, замена константой, псевдонимизация (md5), faker, перестановка, обобщение.
- Email псевдонимизируют через md5 — уникальность сохраняется, восстановить нельзя.
- Полный сценарий: копия прода → скрипт анонимизации → дамп → удалить базу-анонимизатор.
- Скрипт анонимизации хранится в репозитории и проходит ревью.
- Re-identification — реальная угроза: маскировка имени недостаточна, если остались точные дата рождения и город.
Что почитать дальше
- Резервное копирование PostgreSQL — как делать дамп и восстанавливать базу.
- Расширения PostgreSQL — установка и управление расширениями, включая postgresql_anonymizer.