Реализация шифрования локальных данных в мобильном приложении
Мы уже 5+ лет внедряем шифрование локальных данных на iOS и Android. Заказчики часто приходят с проблемой: после взлома устройства данные пользователей оказываются в открытом доступе. Причина проста — шифрование либо не реализовано, либо реализовано с ошибками. SQLite-база без шифрования на Android — это просто файл: adb pull /data/data/com.yourapp/databases/app.db на рутованном устройстве — и все данные читаются любым SQLite-браузером. На iOS ситуация чуть лучше из-за Data Protection API, но только если разработчик не забыл выставить правильный NSFileProtectionKey — а про это забывают часто. Мы гарантируем, что после нашей работы ваши данные останутся защищёнными даже при физическом доступе к устройству.
Что и чем шифруем
Задача делится на три независимых слоя: база данных, файлы, секреты.
База данных. Стандарт — SQLCipher. Форк SQLite с прозрачным AES-256 шифрованием на уровне страниц. На Android подключается через net.zetetic:android-database-sqlcipher, на iOS через SQLCipher.xcframework. Room на Android умеет работать с SQLCipher через SupportOpenHelperFactory — переключение занимает буквально замену фабрики в Room.databaseBuilder() и добавление ключа. Ключ генерируется один раз, сохраняется в Keystore/Keychain, никогда не хранится в SharedPreferences или UserDefaults в открытом виде.
При первом запуске на Android:
val key = generateAes256Key() // через KeyGenerator с KeyStore provider val encryptedKey = encryptWithKeystore(key) // RSA/AES через AndroidKeyStore prefs.putString("db_key_enc", Base64.encode(encryptedKey)) Потом при каждом открытии базы расшифровываем ключ и передаём в SQLCipher. Без PRAGMA key база просто не открывается.
Файлы. Для изображений, PDF, кэша — AES-256-GCM через javax.crypto.Cipher на Android или CryptoKit.AES.GCM на iOS (Swift 5.5+). GCM важен: он даёт и конфиденциальность, и аутентификацию целостности. CBC без MAC — плохой выбор, уязвим к атакам на padding.
На Flutter удобен пакет flutter_secure_storage для секретов и encrypt для файлов, но под капотом оба используют те же нативные API — обёртки, не замена.
Секреты (API-ключи, токены). Только Keychain (iOS) и Android Keystore. Не UserDefaults, не SharedPreferences, не AsyncStorage в React Native. Keychain на iOS шифруется ключами, привязанными к Secure Enclave; Keystore на Android с API 23+ привязывает ключи к TEE или SE — экспортировать их нельзя даже рутом.
Почему важно шифровать именно базу данных?
База данных — самое уязвимое место. В ней обычно хранятся пользовательские профили, списки покупок, история операций. Если злоумышленник получил физический доступ к устройству (утеря, кража) и обошёл блокировку экрана, незашифрованная база — полный доступ к данным. SQLCipher делает базу нечитаемой без ключа. Даже при анализе дампа оперативной памяти ключ не всплывает, так как он хранится в аппаратном Keystore.
Типичные ошибки, которые ломают всю схему
Первая — неправильный класс защиты на iOS. FileProtectionType.complete означает: файл недоступен, пока устройство заблокировано. Но если приложение получает push-уведомление в фоне и пытается читать базу — крэш. Разработчики в панике меняют на completeUnlessOpen или вообще убирают защиту. Правильное решение — разделить данные: критичные под .complete, фоновые операции под .completeUnlessOpen.
Вторая — хранение ключа рядом с данными. Встречали кейс, где ключ шифрования базы лежал в той же директории, что и зашифрованная база, просто в файле key.bin. Это не шифрование, это переименование.
Третья — использование пароля пользователя напрямую как ключа. AES требует 128 или 256 бит. Пароль «qwerty123» — это не ключ. Нужен KDF: PBKDF2 с минимум 100 000 итераций или Argon2id. На iOS — CommonCrypto.CCKeyDerivationPBKDF, на Android — SecretKeyFactory с PBKDF2WithHmacSHA256.
| Слой | iOS | Android |
|---|---|---|
| База данных | SQLCipher + CoreData | SQLCipher + Room |
| Файлы | CryptoKit AES-GCM | javax.crypto.Cipher AES-GCM |
| Секреты | Keychain (Secure Enclave) | Android Keystore (TEE/SE) |
Интеграция с биометрией
Продвинутый вариант — ключ шифрования базы защищён биометрией через Keystore/Keychain. На Android: KeyGenParameterSpec.Builder с .setUserAuthenticationRequired(true) и .setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG). Ключ создаётся один раз при первом входе с биометрией, потом при каждом открытии приложения пользователь проходит аутентификацию, ключ разблокируется из Keystore, база открывается.
На iOS аналогично через kSecAttrAccessControl с SecAccessControlCreateWithFlags и флагом .biometryAny или .biometryCurrentSet. Разница: .biometryCurrentSet инвалидирует ключ при добавлении новых отпечатков — это важно для банковских приложений (согласно официальной документации Apple).
Что входит в работу
Полный цикл внедрения шифрования под ключ:
- Инвентаризация хранящихся данных и классификация по критичности.
- Выбор схемы ключей и интеграция с Keystore/Keychain.
- Реализация слоя шифрования для базы, файлов и секретов.
- Миграция существующих данных (если есть).
- Тестирование всех сценариев: обновление приложения, восстановление из бэкапа, смена биометрии.
- Документация и обучение вашей команды.
Процесс
Начинаем с инвентаризации: что и где хранится, есть ли уже шифрование, какие данные попадают под требования (PCI DSS, GDPR, локальные нормативы). Дальше — выбор схемы ключей, реализация слоя шифрования, интеграция с существующим хранилищем. Отдельно — тестирование сценариев: обновление приложения, восстановление из бэкапа, смена биометрии пользователем.
Срок зависит от объёма данных и наличия существующей схемы хранения. Если база уже есть и нужна миграция на SQLCipher — 3–5 дней с учётом тестирования. Шифрование файлового кэша и Keychain-интеграция — дополнительно 1–2 дня. Полная схема с нуля для нового проекта — быстрее.
Оценим ваш проект бесплатно. Свяжитесь с нами — мы проанализируем текущую схему хранения и предложим оптимальное решение с гарантией безопасности.







