Логи сливают токены: как QA проверить диагностику на утечки
Логин работает, чужие заказы недоступны, после выхода сессия закрывается. Можно ставить галочки. Затем случается таймаут, HTTP-клиент добавляет запрос в исключение, а обработчик отправляет исключение в систему диагностики. Вместе с заголовком Authorization.
Это условный пример, а не история конкретного инцидента. Но он хорошо показывает отдельный предмет тестирования: что приложение рассказывает о пользователе, когда пытается объяснить свою ошибку. Функциональная проверка авторизации сама по себе на этот вопрос не отвечает.
Ниже — предлагаемый практический план для QA. Его нужно адаптировать к архитектуре продукта и согласованным правилам обработки данных. Названия полей и сценарии здесь учебные; реальные пользовательские секреты для проверки не нужны.
Сначала договориться, что считается утечкой
OWASP Logging Cheat Sheet рекомендует не записывать напрямую пароли, access-токены, значения идентификаторов сессий, ключи и другие чувствительные данные. Для персональных данных нужен осознанный выбор состава и обработки. При этом журнал должен сохранять информацию, необходимую для расследования событий; сами механизмы логирования тоже требуют проверки.
Из этого не следует правило «удалить из диагностики вообще всё». С командой стоит составить небольшой контракт: какие события нужны, какие поля разрешены, какие значения запрещены и где выполняется очистка. Например, результат попытки входа нужен, а введённый пароль — нет. Для связи запросов можно использовать отдельный идентификатор корреляции, который сам не даёт доступа к аккаунту.
Разделите секреты и идентификаторы. Полный bearer-токен потенциально позволяет выполнить запрос от имени пользователя. Внутренний user_id обычно решает другую задачу, но всё равно может связывать события с человеком. Решение о его передаче принимается отдельно. Фраза «мы захешировали email» тоже не заменяет проверки: что именно отправляется, кто это видит и зачем это нужно?
Запишите ожидаемый результат до запуска теста. Иначе QA найдёт email в событии, разработчик назовёт его необходимым, а спор закончится обсуждением вкусов вместо проверки требований.
Нарисовать маршрут одного события
Возьмите один сценарий, например ошибку обновления сессии, и перечислите места, через которые проходит диагностика. Возможная цепочка: приложение, локальный лог, очередь SDK, приёмник телеметрии, хранилище, интерфейс просмотра, экспорт для поддержки.
У серверного запроса может быть другая цепочка: reverse proxy, приложение, сборщик логов, индекс, алерт. Не предполагаем, что каждое звено записывает тело запроса. Проверяем конфигурацию и реальные записи каждого участвующего компонента.
Для каждого места отметьте владельца, способ проверки и правило очистки. Если доступа к серверному хранилищу нет, это ограничение покрытия. Чистый экран мобильной консоли не даёт оснований написать «утечек нигде нет».
Особенно полезно разделять очистку перед отправкой и маскирование при показе. Если интерфейс рисует звёздочки, а экспорт отдаёт исходное значение, исходное значение всё ещё хранится. Это два разных результата, и в отчёте их нельзя объединять.
Подготовить синтетические маркеры
Для тестового аккаунта задайте заметные значения: email вида [email protected], пароль с уникальной тестовой последовательностью, отдельную метку в свободном поле профиля. Для каждого типа данных используйте свою метку, чтобы найденная строка сразу указывала на источник.
Поддельный access-токен пригоден для проверки отказа авторизации, но не заменяет тест успешного запроса. Для успешного пути понадобится сессия специально созданного тестового аккаунта в тестовой среде. Такой токен всё равно остаётся секретом: его не нужно вставлять в публичный тикет или демонстрационный скриншот.
Добавьте независимый разрешённый маркер события, например correlation_id. Он нужен для положительного контроля: доказать, что конкретное событие вообще попало в проверяемую систему. Отсутствие пароля в отчёте, который не был доставлен, ничего не говорит о качестве очистки.
Зафиксируйте время прогона, сборку, окружение и настройки SDK. Это позволит отличить новые события от старых и повторить проверку после исправления. Если события приходят с задержкой, заранее определите окно ожидания по конфигурации вашей системы.
Проверить ошибочные пути отдельно
Составьте матрицу из успешного входа, неверного пароля, истёкшей сессии, отказа в доступе, таймаута и ответа 500. Для каждой строки сравните поля диагностики. Цель — заметить момент, когда компактная запись превращается в полный дамп запроса.
Полезный сценарий: сначала выполнить успешное действие, затем повторить его с контролируемой ошибкой зависимости. Продукт должен сохранить диагностическую причину сбоя, но не начать копировать секреты из заголовков и тела только потому, что включился error-handler.
Проверьте ошибку разбора ответа. Если вместо ожидаемого JSON пришёл неожиданный текст, может сработать другой обработчик. В учебном стенде подставьте ответ с синтетическим маркером и посмотрите, где он появится. Ожидание задаётся контрактом: некоторые данные ответа разрешены, другие требуют удаления.
Отдельно вызовите поддерживаемое тестовое исключение для crash-reporting. Проверьте поля события, дополнительный контекст и breadcrumbs, если ваш SDK их собирает. Успешная очистка обычного лога не доказывает, что другой канал диагностики использует тот же фильтр.
Проверить форму данных, а не только название поля
Начните с простого объекта password, затем повторите тест с вложенным объектом credentials.password и массивом объектов. Если приложение действительно принимает несколько форматов, пройдите каждый поддерживаемый вариант. Не нужно придумывать сотню структур, которые никогда не проходят через продукт.
Для заголовков проверьте поддерживаемые представления и регистр имени. Для URL — отдельно путь и query-параметры. Для исключения — message и дополнительный контекст. Значение, скрытое в структурированном поле, может повторяться в свободной текстовой строке.
Поиск одной буквальной строки не покрывает все преобразования. Если ваш pipeline URL-кодирует, сериализует или иначе преобразует данные, проверяйте соответствующее представление. При этом не называйте любое кодирование защитой: возможность восстановить исходный секрет остаётся отдельным критерием.
Здесь особенно помогает таблица «вход — место назначения — ожидаемый результат». Например: синтетический пароль во вложенном поле — событие ошибки — поле отсутствует либо заменено согласованной заглушкой. Такая формулировка воспроизводима и не зависит от того, нравится ли кому-то вид звёздочек.
Смена аккаунта и отложенная отправка
Войти под пользователем A, выполнить действие, выйти, войти под B, вызвать новую ошибку. Проверить, что событию B не присвоен контекст A. Это отдельная проблема даже тогда, когда никаких паролей в событиях нет: диагностика начинает неверно связывать действия с аккаунтами.
Усложните сценарий отключением сети. Создайте событие A офлайн, смените аккаунт, создайте событие B, затем восстановите соединение. Старое событие A может законно сохранить контекст момента создания — это не обязательно дефект. Проверяем, чтобы новые события B не унаследовали A, а уже созданные события не переписывались случайно под текущего пользователя.
После перезапуска приложения повторите проверку, если SDK сохраняет очередь на диске. В отчёте укажите, какие события были созданы до выхода, какие после, и какое поведение согласовано. Без этой временной линии легко принять корректную отложенную отправку за утечку чужой сессии.
Релизная конфигурация и пакет поддержки
Прогоните выбранные сценарии на сборке с настройками диагностики, соответствующими релизу. Это не означает тестировать на реальных аккаунтах в production. Нужны контролируемая среда и понимание отличий: уровень логирования, включённые интеграции, фильтры, sampling, очередь отправки.
Кнопка «Отправить диагностику» заслуживает собственного кейса. Сформируйте пакет, откройте архив и проверьте состав файлов. Сравните его с тем, что показывал интерфейс: экспорт может собираться отдельным кодом и содержать больше данных.
Если приложены скриншоты, проверьте видимые поля и уведомления. Очистка JSON не скрывает секрет, уже попавший в изображение. Для отчёта достаточно синтетической метки; не надо воспроизводить проблему с чужой перепиской или настоящим платёжным документом.
Проверьте ещё один практический момент: не копирует ли сама тестовая инфраструктура найденное значение в CI-лог, attachment или текст падения проверки. Иначе тест на отсутствие секретов создаст дополнительную копию секрета.
Как автоматизировать и не получить ложный зелёный результат
Для старта достаточно одного критичного потока: вход, контролируемая ошибка, получение события по correlation_id, проверка разрешённых полей и отсутствия запрещённых значений. Автоматизация должна различать три исхода: событие чистое, событие содержит запрещённые данные, событие не удалось проверить.
Третий исход не превращаем в успех. Причиной может быть sampling, неверный проект, задержка доставки, отключённый SDK или сломанный тестовый приёмник. Сначала подтверждаем доставку, затем оцениваем состав. При настройке тестового sampling документируем отличие от релиза и отдельно проверяем реальную конфигурацию.
В assertion-message выводите название поля, идентификатор события и тип нарушения. Полное запрещённое значение выводить не нужно. Доступ к подробному артефакту ограничивается так же, как доступ к самой диагностике.
Набор проверок расширяйте по найденным дефектам: вложенные структуры, очереди, смена аккаунта, другой обработчик исключений. Универсальный regex по всем файлам может быть полезным дополнительным сигналом, но он не заменяет проверку конкретного маршрута данных и разрешённого состава события.
Что положить в баг-репорт
Хороший репорт содержит сборку, конфигурацию, минимальные шаги, тип чувствительного поля, место обнаружения и ожидаемое правило. Например: «После таймаута обновления сессии значение Authorization сохраняется в extra.request.headers события; контракт запрещает передачу этого поля».
Для демонстрации используйте синтетическую метку или отредактированный фрагмент. Укажите, проверяли ли исходный payload, сохранённое событие, интерфейс или экспорт. «В интерфейсе скрыто» и «никогда не отправлялось» — разные утверждения.
Оценку влияния связывайте с тем, что обнаружено: даёт ли значение доступ, кто может его прочитать, как долго оно живёт, какие копии уже созданы. Не каждый идентификатор равен паролю, но и слово debug не делает хранилище безопасным автоматически.
После исправления повторите исходный сценарий и проверьте полезность диагностики. Остались ли код ошибки, версия приложения, время и связь событий? Удаление всего события вместе с секретом может закрыть одну проверку и сломать расследование следующего инцидента.
Короткий чек-лист перед релизом
- Для критичного потока определены разрешённые и запрещённые поля.
- Есть синтетические маркеры и положительный контроль доставки.
- Проверены успешный путь, таймаут, отказ авторизации и серверная ошибка.
- Учтены вложенные поля, URL, сообщения исключений и дополнительные каналы.
- Смена аккаунта не смешивает контексты новых событий.
- Проверены отложенная отправка и диагностический экспорт, если они есть.
- Конфигурация соответствует релизу, отличия явно записаны.
- Автотест не печатает найденные секреты в собственный отчёт.
- После очистки событие остаётся пригодным для расследования.
Критерий готовности простой: причину сбоя можно расследовать, а секреты при этом не копируются в диагностику. Доказывать нужно обе части.