Мобильные
Безопасное хранение чувствительных данных в мобильном приложении: Keystore и Keychain
Хранение токенов и персональных данных в SharedPreferences или UserDefaults экспертиза считает высоким риском. Как правильно использовать Android Keystore и iOS Keychain.
· 6 мин. чтения

Одна из Android-находок в экспертизе нашего собственного приложения была короткой и точной: «чувствительные данные хранятся во внутреннем хранилище без шифрования». Уровень риска — высокий. Приложение хранило токен доступа в обычном файле настроек. На обычном телефоне другое приложение этот файл прочитать не может, но на рутованном устройстве или через резервную копию он виден открытым текстом.
В этой статье разберём, что, где и как хранить в мобильном приложении.
Типичная ошибка: «внутреннее хранилище и так закрыто»
Многие разработчики рассуждают так: папка приложения находится в песочнице, другие приложения туда не попадут, значит, шифровать не обязательно. Это верно только в идеальных условиях. На практике данные из внутреннего хранилища могут утечь так:
- Root или jailbreak — пользователь с максимальными правами может прочитать папку любого приложения.
- Резервная копия — если на Android включён
allowBackup, данные приложения попадают в резервную копию и могут быть восстановлены на другом устройстве. - Физический доступ и форензика — если устройство попало в чужие руки, открыто сохранённые данные извлечь проще.
- Другая уязвимость в приложении — например, WebView или content provider с неправильным доступом к файлам.
Поэтому группа STORAGE в MASVS требует хранить чувствительные данные в защищённых механизмах платформы. В OWASP Mobile Top 10 2024 это категория M9 (Insecure Data Storage).
Что считается чувствительным
- Токены доступа (access) и обновления (refresh), идентификаторы сессий.
- API-ключи и ключи шифрования.
- Персональные данные: ПИНФЛ, паспортные данные, номер телефона, финансовые показатели.
- Данные, связанные с биометрией или PIN-кодом.
Пароль пользователя на устройстве не нужно хранить вообще. Вместо него хранится выданный сервером токен с ограниченным сроком действия.
Инструменты платформы
Android Keystore
Android Keystore — система хранения криптографических ключей. Её главное преимущество: ключ не попадает в память приложения. На многих устройствах ключ хранится на аппаратном уровне (TEE или StrongBox). Приложение просит зашифровать данные ключом, но сам ключ не видит. Зашифрованные данные могут лежать в обычном файле — без ключа они бесполезны.
iOS Keychain
iOS Keychain — системное зашифрованное хранилище для паролей, токенов и ключей. Для каждой записи задаётся условие доступа. Например:
WhenUnlockedThisDeviceOnly— данные читаются только когда устройство разблокировано и не переносятся на другое устройство через резервную копию.AfterFirstUnlockThisDeviceOnly— данные доступны после первой разблокировки после перезагрузки. Удобно для фоновых запросов.
Суффикс ThisDeviceOnly важен: он не даёт записи перенестись на другое устройство через резервную копию iCloud.
На практике во Flutter: flutter_secure_storage
В нашем Flutter-приложении мы использовали пакет flutter_secure_storage. На Android он использует шифрование на основе Keystore, на iOS — Keychain, а код один для обеих платформ:
const storage = FlutterSecureStorage(
iOptions: IOSOptions(
accessibility: KeychainAccessibility.first_unlock_this_device,
),
);
await storage.write(key: 'access_token', value: token);
final token = await storage.read(key: 'access_token');
Вместе с этим были внесены следующие изменения:
- Все токены перенесены из
SharedPreferences.SharedPreferencesостался только для нечувствительных настроек: язык, тема, просмотрен ли онбординг. - Резервное копирование отключено. В
AndroidManifest.xml:
<application
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules">
data_extraction_rules управляет облачным резервным копированием и переносом между устройствами на Android 12 и выше.
- При выходе всё очищается. Когда пользователь выходит из системы, записи в защищённом хранилище удаляются, также очищаются кеш, cookie и сохранённые данные WebView. Иначе следующий пользователь может найти следы предыдущей сессии.
Защищённое хранилище защищает данные на диске. Но оно не защищает то, что приложение держит в памяти во время работы. Поэтому оно должно работать вместе с RASP и обнаружением root.
Тонкие моменты
- iOS Keychain может сохраняться после удаления приложения. Если пользователь удалит и заново установит приложение, старый токен может найтись. Решение: при первом запуске проверяйте простой флаг и, если его нет, удаляйте старые записи из Keychain.
- В логах не должно оставаться чувствительных данных. В release-сборке токены, тела запросов и персональные данные не должны писаться в лог.
- Кеш и временные файлы. Скачанные документы, PDF-отчёты и изображения тоже могут быть чувствительными. Удаляйте их после использования.
- Скриншоты. На чувствительных экранах можно использовать
FLAG_SECUREна Android и скрывать содержимое на iOS при уходе приложения в фон.
Проверьте сами
- На рутованном тестовом устройстве откройте папку приложения (
/data/data/<пакет>/) и поищите открытые токены или персональные данные вshared_prefs, базах данных и файлах. - Проверьте, не утекают ли данные через
adb backupили перенос устройства. - Убедитесь, что после выхода из системы в папке приложения и данных WebView не остаётся следов сессии.
- На iOS удалите и заново установите приложение и проверьте, что автоматического входа не происходит.
Вывод
Безопасное хранение чувствительных данных — несложная задача: платформа даёт для этого готовые инструменты. Основная работа — выяснить, где хранятся данные, перенести их в Keystore или Keychain и правильно настроить резервное копирование и очистку при выходе.
Если хотите проверить, как ваше приложение хранит чувствительные данные, оставьте заявку на сканирование. Мы найдём уязвимости и поможем закрыть их до экспертизы.


