← назад к разделу

Разработчику нужно воспроизвести баг, который виден только на реальных данных. Первое желание — скинуть дамп производственной базы. Проблема: это нарушение закона о персональных данных (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

Псевдонимизация: одинаковый 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.