Как я делал и делал бы поддержку gdpr
Удалось столкнуться с этой штукой в момент её появления и в авральном режиме впилить в крупную мобильную игру. Поскольку делать эту функциональность мне явно придётся ещё не раз, а также потому, что иногда спрашивают про неё запишу тут свои мысли о практической реализации поддержки этого закона.
Без юридических тонкостей, так как они не моя специальность, могу насоветовать не того. В целом, с подозрением относитесь к любым не программистским утверждениям в этом тексте, их надо перепроверять!
Про GUI я тут тоже говорить не буду.
Этот текст не отражает ни конкретную реализацию, ни идеальную, а является скорее собранием накопившегося опыта и мыслей.
Что такое GDPR
GDPR — европейский закон о защите данных, который требует от разработчиков кошерного обращения с данными пользователя. В том числе:
- Предоставления ему в короткий срок всего собранного на его компромата.
- Удаления компромата по запросу пользования за разумный срок (был месяц, как сейчас не знаю).
Под компроматом имеется в виду вся информация, которая может позволить вам прямо или косвенно идентифицировать человека:
- Личные данные (ФИО, телефон, адрес, фото, аватарка).
- Данные, по совокупности которых можно с большой точностью определить человека: страна, город, IP адрес, MAС адрес, время игры, время платежей, время логина (и аналогичные), суммы платежей, etc.
- Любую привязку к внешним сервисам (и информацию в них). Обычно это идентификаторы в социальных сетях, магазинах, системах аналитики, сервисах поддержки.
Кстати, сейчас GDPR принимает некое собирательное значение, поскольку каждый царёк на своей территории вводит похожий закон.
Общее решение
При реализации я принял решение делать отдельный полностью независимый сервис, который умеет:
- собирать данные из любой дыры;
- инициировать удаление данных и дожидаться его;
- отображать собранные данные пользователям и удалять их через какое-то время.
Всё взаимодействие с сервисом происходит через HTTP API.
Основная аргументация «за» следующая:
- Основной продукт — вещь крайне динамичная, он постоянно изменяется, имеет кучу легаси, над ним в разное время работают разные люди с разной целью и разной компетентностью.
- Поэтому введение сложных внутренних политик и правил станет дорогим удовольствием и приведёт к разнобою в реализациях и костылям.
- Кроме того, из-за перманентного бардака со сбором данных (не тыкайте в нас пальцем, у вас точно также), не желательно прокидывать лишнее взаимодействие со внешними сервисами во все места, где в них что-то отправляется.
- В целом, внешние сервисы имеют крайне разное API в целом, и особенно упоротые реализации логики GDPR, делать взаимодействие с ними из случайных мест проекта — крайне плохая идея.
- Проще потребовать от каждой внутренней сущности (микросервиса, например) реализации простого протокола, по которому с ним будет взаимодействовать внешний специализированный сервис.
- Этот же сервис проще будет научить взаимодействовать со внешними сервисами с учётом всех их нюансов.
Протокол в итоге получается примерно таким.
Каждый наш сервис предоставляет следующие методы:
- Получить данные и внешние идентификаторы игрока с внутренним идентификатором X.
- Удалить данные и внешние идентификаторы игрока с внутренним идентификатором X.
Сервис GDPR предоставляет следующие методы:
- Инициировать сбор данных для игрока с внутренним идентификатором X и внешними идентификаторами такими-то.
- Инициировать удаление данных для игрока с внутренним идентификатором X и внешними идентификаторами такими-то.
- Получить собранные данные по такому-то случайному уникальному идентификатору (uuid4, например).
Подобный сервис, в целом удобно делать через систему плагинов (один плагин на тип внешнего сервиса), где логика взаимодействия с вашим продуктом является просто одним из плагинов (или несколькими, например, один для основного сервера, другой для хранилища аналитики).
Как данные собирать
При сборе данных наш сервис обходит все занесённые в конфиг сущности и просит у них данные через протокольный метод (если сущность наша) либо через самое уникальное и удобное API внешнего сервиса.
Результат каждого обращения сервис хранит у себя в базе, пока не получит все данные. Это нужно на случай ошибок и любых других проблем. Гарантирую, что за один проход вы не сможете успешно собрать все данные, особенно поначалу. Внешние API будут падать и менять формат. Внутренние API будут ломаться, например, из-за китайского юникода, непредвиденных NULL в базе и ошибок деплоя. Кроме того, сбор некоторых данных может просто занимать много времени.
Фактически, уже при поступлении запрос дробится на атомарные операции по взаимодействию с каждой сущностью в отдельности, чтобы они никак не мешали выполнению друг друга.
Раньше (как сейчас не знаю) GDPR не требовал какой-то особый формат отчёта для пользователя, лишь бы был читаемым. Это существенно упрощает дело.
В итоге я пришёл к представлению всех данных о пользователе в виде кортежа <источник, тип данных, данные в текстовом виде>
. Например:
<игра, IP адресс, “127.0.0.1”>
<игра, имя, “super killer“>
<Adjust, adid, “a67bd55-377e-46b0-9bf3-15455e22f5ae”>
<HelpShift, сырые данные, “{длинная и стрёмная JSON-ка}”>
Наши сервисы отдают данные сразу в нужном формате. Данные из внешних сервисов мы приводим к нему с удобной нам детализацией.
Подобная организация позволяет «красиво» сформировать простой читаемый HTML отчёт. Например, сгруппировать записи по источнику и отобразить отсортированным по типу данных списком, попутно убрав дубликаты и пустые значения.
Когда все данные собраны, сервис формирует HTML файл с уникальным именем и ложит его в публичную директорию, доступную по HTTP. Файлы из этой директории удаляются, например, через три дня после создания.
Хочу обратить внимание, что уникальное имя файла необходимо сформировать при обращении за данными, чтобы сразу вернуть готовый url пользователю. Если пользователь захочет получить ещё один отчёт, то надо инициировать новую операцию, даже если старая не завершена, так как данные могли обновиться. При этом старую останавливать не надо.
Как данные удалять
С точки зрения сервиса, логика удаления в целом эквивалентна логике сбора данных, за исключением кучи нюансов :-) Вот некоторые из них:
- Сразу (то есть в момент инициации операции пользователем) необходимо удалить все данные, которые могут позволить пользователю войти в приложение любым способом. Обязательно разлогинить его. Это покажет, что мы начали удаление данных.
- Не надо делать никаких отчётов и сообщений для пользователя.
- Логика игры должна быть устойчива к работе с частично удалёнными аккаунтами (например, что будет, если вы начнёте удалять данные в процессе пересчёта рейтингов?). Возможно, потребуется задать явный порядок обращения к внутренним и внешним сущностям.
Раньше, удаление данных требовалось в течении месяца (как сейчас не знаю). Поэтому самым крутым способом удаления данных является введение политики по которой никакие данные не живут дольше месяца.
По крайней мере такая политика может быть полезна в частных случаях.
Примером может стать сбор аналитики. Фактически в неё шлётся всё, всеми и отовсюду. Поэтому в любом месте вашей петабайтной помойки могут затесаться если не личные данные, то косвенные признаки пользователей. Гарантировано подчистить их нереально. К тому же, постоянные выборки по всей истории событий могут быть дорогими и долгими. Поэтому разумным видится установить время жизни некоторых событий в месяц, либо выделить «опасные» данные в отдельные таблицы.
Нюансы и советы
Тут соберу просто полезные примечания по теме.
Помните о принципе неуловимого Джо. Не загоняйтесь сверх меры. Пока с вас нечего брать, вряд ли на вас обратят внимание. Следуйте этому совету на свой страх и риск, за следование ему я ответственности не несу :-)
Лучше удалить/вернуть больше информации, чем меньше, имхо.
Помните, что данные имеют тенденцию расползаться по компании. Они проникают в почту, в бэкапы, просто на компы ваших сотрудников. Удалить оттуда их нереально, бороться с этим можно только правильным рабочим процессом и понятными политиками доступа к данным.
Кроме серверов у вас есть клиентские приложения. Они тоже считаются вашими хранилищами данных. А значит:
- Данные из них надо предоставлять в отчёте.
- Данные из них нужно удалять.
У пользователя может быть несколько клиентских приложений на разных устройствах.
Одним клиентским приложением могут пользоваться несколько пользователей. Не раскройте/удалите чужие данные.
Полезно хранить список внутренних идентификаторов удалённых пользователей. Очень помогает отвечать на вопросы вроде: «что за !”№!”№!@ с этим аккаунтом????!!!! ».
Также полезно затирать данные чем-то осмысленным, поскольку тот же NULL в базе может появиться разными способами, а значит может означать разное. Явное указание на причину поможет при отладке. Например, вместо пустой строки можно использовать “removed due GDPR”.
Наладьте хорошую нотификацию об ошибках (как в основном продукте, так и в сервисе), иначе рискуете внезапно обнаружить, что не выполняли запросы на удаление данных уже год. Кроме того, удаление данных само по себе источник постоянных багов.
С точки зрения логики продукта не пытайтесь реально удалять информацию (записи в базе, например), аналитики не скажут за это спасибо, да и просто это очень сложно. Просто затирайте всё нужное, оставляя ненужное как есть. Будет у вас немного мёртвых аккаунтов, не страшно.
Разрешать пользоваться продуктом без сбора данных, на мой взгляд, плохая идея. Если этой фичёй пользоваться не будут, то делать её дороже выйдет. Если будут, то она исказит вам всю аналитику.
Выделять отдельного человека, который следит за исполнением требований подобных законов имеет смысл только для больших компаний. В маленьких это должно происходить через внедрение правильной культуры производства под присмотром лидов и технического директора в особенности.
С другой стороны, у самого сервиса обязательно должен быть конкретный владелец, который будет холить его и лелеять. То есть у человека должно хватать времени на поддержку сервиса. Маленький косяк в работе логики GDPR, может обанкротить вашу компанию.
В целом, политика работы с личными данными должна существовать уже при начале разработки, иначе при её последующем внедрении будет огромное количество проблем. И чем слабее команда, тем больше проблем будет. Для примера, любой код должен быть готов к отсутствию любых данных о пользователе.
Автоматические тесты наше всё. Каждый кусочек логики должен автоматически проверяться на корректную работу при удалении данных.
Ревью кода тоже наше всё. Люди склонны забывать о необходимости удалять и возвращать данные. Вопросы вроде: «А какие новые данные пользователя ты тут используешь?», «Почему не изменился код метода удаления/получения данных?» будут задаваться часто.
Вопросы
Задавайте вопросы в комментариях. По мере сил буду отвечать и дополнять пост.
P.S.
Сделал в Сказке управление пользовательскими данными:
162 files changed, 7845 insertions(+), 769 deletions(-)
Если начинаете новый проект, делайте сразу с этими фичами. Иначе будете страдать.