Где в записи сессии оказываются чужие персональные данные, даже если пользователь ничего не вводил, почему маскировать их нужно ещё в браузере и какой режим маскирования безопаснее по умолчанию.
Привет! Я Паша, работаю над российским сервисом записи сессий пользователей. В этой статье я расскажу, как мы добиваемся соответствия продукта российским законам с точки зрения персональных данных, что в нашей нише не так уж просто. И я не юрист: юридическая часть ниже описывает, как мы это поняли. Проверяйте со своим юристом.
Первый вопрос, который задают про запись сессий: «а вы видите, что я ввожу в поле?». Честный ответ: технически скрипт записи видит всё, что видит браузер. Вопрос только в том, что он отправит на сервер.
Ниже о том, где обычно утекает лишнее, как с этим работать и при чём здесь 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, поведение на сайте и технические данные устройства.

