Удаление аккаунта с периодом возражения (мягкое удаление + анонимизация)
Коротко: Вместо того чтобы сразу безвозвратно удалять аккаунт при запросе удаления, сначала фиксируется лишь момент времени (pending_deletion_at) — аккаунт фактически анонимизируется только по истечении срока.
Подробнее: Немедленное жёсткое удаление рискованно: случайный клик, взломанный аккаунт или отзыв запроса на удаление иначе уже нельзя было бы отменить. Срок оставляет место для «отмены», не противореча праву на удаление (ст. 17 GDPR) — по истечении срока персональные данные удаляются/анонимизируются (anonymizedAt фиксирует, КОГДА это произошло).
Наш контекст: В Emzett db/migrations/add_v27_session_version_deletion.sql добавляет users.pending_deletion_at и users.anonymized_at — 7-дневный период возражения перед фактической анонимизацией.
Подробности
Не юридическая консультация, а техническая классификация. Право на удаление (ст. 17 GDPR) не обязательно требует немедленного жёсткого удаления — оно требует, чтобы персональные данные были удалены без неоправданной задержки, когда цель хранения отпала. Короткий, ясно сообщённый период возражения с чёткой инструкцией, как можно отозвать запрос на удаление, как правило, считается допустимым, если он не используется для фактического затягивания удаления.
Технически различают два этапа: мягкое удаление (запись полностью сохраняется, лишь флаг/метка времени отмечает её как «запланированную к удалению» — обратимо) и анонимизация (персональные поля заменяются заменителями, которые больше нельзя отследить — необратимо). Важно для настоящей анонимизации: недостаточно удалить только очевидные поля вроде имени/электронной почты — косвенные идентификаторы (уникальная комбинация даты рождения + почтового индекса, связанная, в остальном пустая история заказов) тоже могут сделать человека повторно идентифицируемым, если их оставить.
-- Запланировать (обратимо)
UPDATE users SET pending_deletion_at = now() + interval '7 days' WHERE id = $1;
-- После истечения срока (необратимо, через cron-задачу)
UPDATE users SET email = 'deleted-' || id || '@anon.local', name = 'Deleted user', anonymized_at = now()
WHERE pending_deletion_at < now() AND anonymized_at IS NULL;См. также: Экспорт данных по GDPR (ст. 15/20), GDPR