Где в записи сессии оказываются чужие персональные данные, даже если пользователь ничего не вводил, почему маскировать их нужно ещё в браузере и какой режим маскирования безопаснее по умолчанию.

Привет! Я Паша, работаю над российским сервисом записи сессий пользователей. В этой статье я расскажу, как мы добиваемся соответствия продукта российским законам с точки зрения персональных данных, что в нашей нише не так уж просто. И я не юрист: юридическая часть ниже описывает, как мы это поняли. Проверяйте со своим юристом.

Первый вопрос, который задают про запись сессий: «а вы видите, что я ввожу в поле?». Честный ответ: технически скрипт записи видит всё, что видит браузер. Вопрос только в том, что он отправит на сервер.

Ниже о том, где обычно утекает лишнее, как с этим работать и при чём здесь 152-ФЗ.

Что видит скрипт записи

Мы используем библиотеку rrweb для записи экрана, которая сериализует DOM целиком: текст на странице, значения полей, атрибуты элементов. Отсюда несколько мест утечки, и очевидное среди них только одно.

  • Поля ввода. Это знают все.

  • Текст, который уже отрисован на странице. Имя в шапке личного кабинета, баланс счёта, номер договора, последние цифры карты. Пользователь ничего не вводил, а данные в записи есть. Если маскировать только поля ввода, этот текст останется в записи.

  • Textarea и contenteditable. Переписка с поддержкой, комментарии, заметки.

  • Автозаполнение браузера. Значение появляется в поле без единого нажатия клавиши. Обработчик ввода может его не заметить, а снимок DOM заметит.

  • URL. Email или токен в query string попадают в запись вместе с адресом страницы.

Маскировать на клиенте, а не на сервере

Если чувствительное значение ушло с устройства, пусть даже для того, чтобы его вырезали на сервере, оно уже где‑то побывало: в логах балансировщика, в очереди, в бэкапе. Маскирование на сервере по сути удаление уже полученных данных. Правильно, когда данные просто не отправляются. Поэтому мы маскируем в браузере: значение заменяется до отправки и в открытом виде устройство посетителя не покидает.

В rrweb для этого есть штатные опции:

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

Отдельно про текст, который уже отрисован на странице. Маскировать только поля ввода здесь недостаточно: имя, баланс или ответ оператора в чате лежат в обычных блоках. Поэтому принудительное маскирование у нас работает с любым элементом: весь текст внутри него и его потомков заменяется звёздочками ещё в браузере. По смыслу это тот же приём, что maskTextSelector, только правило можно задать и в разметке, и селектором в настройках.

А если данные можно вообще не выводить целиком, это всё равно лучший вариант: последние четыре цифры карты вместо полного номера, частично скрытый номер договора. Такой подход защищает не только от записи сессий, но и от скриншотов, расширений браузера и чужих глаз за спиной.

Что маскируется всегда

Пароли и платёжные поля мы решили маскировать без возможности отключить.

Наш трекер считает поле паролем или платёжным, если выполняется хотя бы одно из условий:

  • Тип поля password.

  • Атрибут autocomplete со значением cc-*: cc-number, cc-csc, cc-exp, cc-exp-month, cc-exp-year, cc-name и другие. Составные значения тоже распознаются, например autocomplete="billing cc-number".

  • Имя или идентификатор поля (name, id, data-name) похожи на платёжные: card_number, cardNumber, cc-num, cvv, cvc, csc, exp_month, expiry_date, cardholder, pan и похожие. Регистр, дефисы, точки и подчёркивания не важны.

  • Подсказка в поле (placeholder, aria-label, title) говорит о карте: «Номер карты», «Срок действия», «ММ/ГГ», «Держатель карты», «Имя на карте», «Код безопасности», «CVV», “CVC”, “Card number”, “Expiry date”, “Security code”.

Решение «без возможности отключить» далось не сразу: на этапе отладки формы оплаты очень хочется видеть, что ввёл пользователь. Но отключаемая защита рано или поздно оказывается отключённой на проде, потому что кто‑то отлаживал форму и забыл вернуть настройку.

Blacklist или whitelist

Дальше выбор режима. Их два, и в каждом нужен обратный рычаг.

Blacklist: записываем всё, кроме помеченного. Запись полезная, видно, что человек вводил и на каком шаге ошибся. Слабое место у этого режима одно: новое поле. Разработчик добавил в форму «Серия и номер паспорта», не повесил класс маскирования, и паспортные данные поехали в записи. Никто этого не заметит, потому что всё работает. Страхует только автоопределение паролей и платёжных полей, которое работает в любом режиме, но паспорт оно не угадает.

Whitelist: маскируем всё, кроме явно разрешённого. Безопасно по умолчанию, но запись превращается в строки звёздочек, и понять, почему пользователь застрял на форме, становится заметно сложнее.

Мы сделали whitelist режимом по умолчанию для всех проектов, не только для финтеха. Аргумент простой: ошибка в whitelist оборачивается звёздочками вместо полезных данных, ошибка в blacklist оборачивается утечкой персональных данных. Первое неудобно, второе необратимо. Поля, которые действительно нужно видеть, открываются точечно. Работать в blacklist можно, но это осознанный выбор, и тогда каждую новую форму стоит проверять на ревью.

Исключения нужны в обе стороны: в whitelist открыть отдельные поля, в blacklist принудительно закрыть любой элемент, не только поле. Мы дали два способа задать такое правило: классом в разметке или CSS‑селектором и data‑атрибутом в настройках проекта. Второй способ нужен на случай, когда утечку нашли уже в проде: закрыть элемент можно селектором, не дожидаясь релиза.

При чём здесь 152-ФЗ

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

Запись сессии, привязанная к email, это обработка персональных данных. Без оговорок. Есть ирония в том, что самая полезная для команды вещь, привязка записи к конкретному пользователю, делает данные чувствительнее. Анонимная запись и запись с email клиента с точки зрения закона различаются сильно.

Персональные данные граждан РФ при сборе должны записываться и храниться в базах на территории России. Если сервис записи держит данные за рубежом, появляется отдельный вопрос трансграничной передачи со своими требованиями.

С 1 сентября 2025 года согласие на обработку персональных данных оформляется отдельным документом. Пункт внутри оферты или политики конфиденциальности больше не подходит.

И важное: маскирование не заменяет согласие. Даже если в записи нет паролей и карт, в ней остаются email, поведение на сайте и технические данные устройства.

Комментарии (0)