Токен сессии, записанный в SharedPreferences, обычный файл или журнал, может попасть в резервную копию, crash-report или на устройство с расширенным доступом. Полностью защитить секрет на скомпрометированном телефоне нельзя, но можно заметно сократить срок и последствия утечки.
Access token делают короткоживущим и по возможности держат в памяти. Refresh token сохраняют через платформенное защищенное хранилище Keychain/Keystore с подходящими параметрами, ротируют на сервере и отзывают при выходе или подозрительной активности.
Что проверить в первую очередь
Сначала зафиксируйте точный сценарий, время ошибки и последнее известное рабочее состояние. Не меняйте несколько настроек одновременно: один контролируемый шаг должен подтверждать или исключать одну гипотезу. Перед работой с данными и конфигурацией подготовьте резервную копию и понятный способ отката.
- Найдите все места записи токена: SharedPreferences, SQLite, файлы, state, логи и crash analytics.
- Разделите access token, refresh token, device id и несекретные настройки.
- Проверьте срок жизни, ротацию и серверный механизм отзыва.
- Уточните настройки backup и accessibility защищенного хранилища на iOS и Android.
Почему возникает проблема
Внешний симптом обычно появляется на границе нескольких компонентов: интерфейса, backend, базы, фоновой очереди или внешнего сервиса. Поэтому важно найти первое место, где состояние становится неверным, а не исправлять последнее сообщение об ошибке.
- Библиотека сохраняет токен как обычную строку в preferences.
- HTTP-interceptor или debug-лог печатает Authorization header.
- Access и refresh token имеют одинаково долгий срок и не ротируются.
- При выходе очищается интерфейс, но токен остается в storage и push-привязке.
- Ключ защищенного хранилища доступен после восстановления backup на другом устройстве.
Пошаговая диагностика
Диагностику проводите на тестовой записи или отдельном окружении. В журналах скрывайте токены, пароли и персональные данные. Для каждого шага сохраняйте измеримый результат: идентификатор события, код ответа, версию записи, состояние процесса или контрольную сумму.
- Выполните поиск по имени ключа и фрагменту тестового токена в файлах sandbox и логах.
- Проверьте release-сборку: debug и production могут использовать разные логгеры.
- Проследите login, refresh, logout, удаление аккаунта и смену пароля.
- Проверьте поведение после перезагрузки, блокировки экрана и переноса резервной копии.
- На сервере найдите активные refresh-сессии и убедитесь, что их можно отозвать по устройству.
Как разделить access и refresh token
Короткий access token снижает окно злоупотребления, а refresh token требует более строгого хранения и серверного контроля.
- Access token можно держать в памяти и получать заново после перезапуска через refresh.
- Refresh token хранится в platform secure storage и привязан к серверной сессии устройства.
- При каждом обновлении refresh token ротируется, а повтор старого значения считается сигналом риска.
- Сервер хранит хеш или идентификатор сессии, срок, устройство и время последнего использования.
- Чувствительные операции могут требовать повторной аутентификации независимо от активной сессии.
Как исправить проблему
Исправление лучше разбить на небольшие обратимые изменения. Сначала устраните подтвержденную причину, затем повторите исходный сценарий и проверьте соседние функции. Массовую обработку данных запускайте на ограниченной выборке с отчетом и только после сверки расширяйте на весь объем.
- Перенесите refresh token в flutter_secure_storage с проверенными платформенными опциями.
- Уберите токены из SharedPreferences, URL, аналитики, логов и текста исключений.
- Сократите срок access token и внедрите ротацию refresh token на backend.
- При logout, смене пароля и удалении аккаунта отзывайте серверную сессию и очищайте storage.
- Добавьте обработку недоступности/сброса Keychain или Keystore без бесконечного цикла входа.
Безопасный порядок внедрения
- Сохраните затрагиваемые данные, конфигурацию и текущие журналы, заранее проверив способ отката.
- Повторите проблему на тестовом объекте без реальных списаний, рассылок и изменений клиентских данных.
- Внесите одно логическое изменение и зафиксируйте его в системе контроля версий или журнале работ.
- Не отключайте авторизацию, валидацию, шифрование и другие защитные механизмы ради быстрого исчезновения ошибки.
- После выкладки контролируйте логи, метрики и полный пользовательский сценарий, а не только один успешный запрос.
Как проверить результат
Разовый успешный тест недостаточен. Повторите операцию, проверьте крайние значения, параллельные действия и восстановление после перезапуска или временного сбоя. Для важного сценария сохраните автоматический тест либо короткий регрессионный чек-лист.
- Тестовый токен отсутствует в preferences, файлах, логах и crash-отчете.
- Выход немедленно делает refresh token недействительным на сервере.
- Повтор старого refresh token после ротации отклоняется и фиксируется.
- Переустановка, восстановление backup и смена устройства не открывают чужую сессию.
Типичные ошибки при исправлении
- Шифровать токен ключом, жестко записанным в коде приложения.
- Считать secure storage абсолютной защитой на root/jailbreak устройстве.
- Логировать полный HTTP request в production.
- Удалять только локальный токен без серверного отзыва сессии.
Как предотвратить повторение
Профилактика строится вокруг явных контрактов, повторяемых релизов и наблюдаемости. Система должна не только работать сейчас, но и позволять быстро увидеть нарушение инварианта при следующем обновлении, росте нагрузки или сбое внешнего сервиса.
- Добавьте статическую проверку логов и секретов перед release.
- Поддерживайте список активных устройств и возможность завершить каждую сессию.
- Мониторьте reuse refresh token и необычную смену контекста.
- Регулярно обновляйте библиотеку secure storage и проверяйте изменения платформенных настроек.
Что подготовить для технического разбора
- Описание ожидаемого и фактического поведения, а также точную последовательность действий.
- Время проблемы, идентификатор тестового объекта и версии затронутых компонентов.
- Фрагменты журналов до и после ошибки без секретов и персональных данных.
- Перечень последних изменений и уже выполненных проверок.
- Безопасный доступ к тестовой среде или способ воспроизвести сбой без влияния на клиентов.
Частые вопросы
Можно ли хранить access token в secure storage?
Можно, но короткоживущий access token часто достаточно держать в памяти. Главное — не превращать его в долгоживущий секрет и не записывать в логи.
Защищает ли certificate pinning украденный токен?
Нет. Pinning защищает часть сетевого канала, но не делает уже украденный bearer token бесполезным. Нужны короткий срок, ротация и отзыв.
Когда нужна помощь специалиста
Если Flutter-приложение хранит токены в preferences или не умеет отзывать сессии, я могу переработать клиентское хранение и серверную ротацию, проверить logout и исключить утечки через логи и резервные копии.