Реалізація шифрування локальних даних у мобільних додатках
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 — експортувати їх неможливо навіть рутом.
Типові помилки, які ломають всю схему
Перша — неправильний клас захисту на iOS. FileProtectionType.complete означає: файл недоступний, поки пристрій заблокований. Але якщо додаток отримує push-уведомлення у фоні та пробує читати базу — крах. Розробники в паніці змінюють на completeUnlessOpen або взагалі видаляють захист. Правильне рішення — розділити дані: критичні під .complete, фонові операції під .completeUnlessOpen.
Друга — зберігання ключа рядом з даними. Я бачив кейси, де ключ шифрування бази лежав у тій же директорії, що й зашифрована база, просто у файлі key.bin. Це не шифрування, це переrename.
Третя — використання пароля користувача напрямки як ключа. AES вимагає 128 або 256 бітів. Пароль «qwerty123» — це не ключ. Потрібен KDF: PBKDF2 з мінімум 100 000 ітерацій або Argon2id. На iOS — CommonCrypto.CCKeyDerivationPBKDF, на Android — SecretKeyFactory з PBKDF2WithHmacSHA256.
Інтеграція з біометрією
Просунутий варіант — ключ шифрування бази захищений біометрією через Keystore/Keychain. На Android: KeyGenParameterSpec.Builder з .setUserAuthenticationRequired(true) та .setUserAuthenticationParameters(0, KeyProperties.AUTH_BIOMETRIC_STRONG). Ключ створюється один раз при першому вході з біометрією, потім при кожному відкритті додатку користувач проходить аутентифікацію, ключ розблоковується з Keystore, база відкривається.
На iOS аналогічно через kSecAttrAccessControl з SecAccessControlCreateWithFlags та флагом .biometryAny або .biometryCurrentSet. Різниця: .biometryCurrentSet інвалідує ключ при додаванні нових відпечатків — важливо для банківських додатків.
Процес
Починаємо з інвентаризації: що й де зберігається, є ли вже шифрування, які дані підпадають під вимоги (PCI DSS, GDPR, локальні нормативи). Далі — вибір схеми ключів, реалізація шару шифрування, інтеграція з існуючим сховищем. Окремо — тестування сценаріїв: оновлення додатку, відновлення із бекапу, змінення біометрії користувачем.
Термін залежить від обсягу даних та наявності існуючої схеми зберігання. Якщо база вже існує та потрібна міграція на SQLCipher — 3–5 днів із врахуванням тестування. Шифрування файлового кешу та Keychain-інтеграція — додатково 1–2 дні. Повна схема з нуля для нового проекту — швидше.







