Реализация шифрования локальных данных в мобильном приложении
Мы уже 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 дня. Полная схема с нуля для нового проекта — быстрее.
Оценим ваш проект бесплатно. Свяжитесь с нами — мы проанализируем текущую схему хранения и предложим оптимальное решение с гарантией безопасности.
Безопасность мобильных приложений: OWASP MASVS, pinning и защита от реверса
Мы провели аудит более 40 мобильных приложений — и на каждом втором находили токены в UserDefaults, отсутствие pinning и открытый для реверса код. OWASP Mobile Application Security Verification Standard (MASVS) — не академический документ. Это чек-лист пентестера. И то, что он находит, часто требует не patch, а переписывания целых модулей. Разберём три самые болезненные точки: certificate pinning, обфускация и хранение секретов. И покажем, как их закрыть без даунтаймов на продакшне.
Почему certificate pinning ломает продакшн?
Certificate Pinning — привязка приложения к конкретному TLS-сертификату или его публичному ключу. Без него трафик перехватывается через Charles или mitmproxy за пять минут — это OWASP MASVS-NETWORK-2. Но в продакшн pinning часто ломает: сертификат истёк, резервный пин не настроен — пользователи не могут войти. Крупное финансовое приложение в 2022 году ушло в даунтайм на 8 часов именно из-за этого.
На iOS реализуется через URLSessionDelegate.urlSession(_:didReceive:completionHandler:) с проверкой SecTrust. Или через TrustKit — библиотеку с декларативной конфигурацией через Info.plist. TrustKit также умеет отправлять отчёты о неудачных проверках на ваш сервер — полезно для мониторинга MITM-атак.
На Android — network_security_config.xml:
<network-security-config>
<domain-config>
<domain includeSubdomains="true">api.example.com</domain>
<pin-set expiration="2026-01-01">
<pin digest="SHA-256">base64_public_key_hash</pin>
<pin digest="SHA-256">backup_key_hash</pin>
</pin-set>
</domain-config>
</network-security-config>
Критическое правило: всегда два пина — основной и резервный. Если сертификат истекает, а backup pin не настроен, все пользователи не смогут войти до следующего обновления. Именно так ломаются production-сборки.
Ещё одна точка отказа: CDN и third-party SDK. Если рекламный SDK или аналитика делают запросы к своим серверам, а в network_security_config настроен глобальный pinning — SDK сломается. Конфигурация должна быть поддоменно-специфичной.
Как защитить данные в Keychain и Keystore?
MASVS-STORAGE-1 и STORAGE-2 — самые часто нарушаемые требования. Частая ошибка на iOS: токены авторизации хранятся в UserDefaults. Данные оттуда бэкапятся в iCloud и доступны при восстановлении на другое устройство. Токен на новом iPhone — это чужая авторизованная сессия. Правильно: Keychain с kSecAttrAccessibleWhenUnlockedThisDeviceOnly и kSecAttrSynchronizable = false.
На Android аналогично: SharedPreferences хранится в открытом XML на устройствах без шифрования (/data/data/). Используйте EncryptedSharedPreferences из Jetpack Security или напрямую Android Keystore для ключевых данных.
Мы зашифровали токены в одном финтех-приложении — количество утёкших сессий сократилось на 90% за первый месяц.
Обфускация и защита кода
iOS: Swift-код компилируется в нативный бинарник, который не декомпилируется до читаемого Swift. Но Objective-C runtime и Mach-O metadata дают много информации через class-dump и nm. Имена классов, методов, строки в бинарнике — всё видно. Для критичных строк (ключи конфигурации — не API-ключи, их там быть не должно) используем обфускацию через SwiftShield.
Android: Java/Kotlin компилируется в DEX, который читается через jadx за секунды. R8 (включён по умолчанию в release сборках) минифицирует и обфусцирует. Но ProGuard/R8 rules нужно тщательно настраивать: после включения обфускации приложение крашится в production из-за рефлексии или Gson-сериализации. Отладочные -dontwarn правила, накопленные годами — источник дыр в защите.
Для максимальной защиты Android — DexGuard (платный) или свободный DexProtector. Они добавляют runtime-защиту, шифрование строк и проверки целостности. Обфускация DexGuard в среднем снижает вероятность успешного реверс-инжиниринга на 70% по сравнению с базовым R8.
Обнаружение jailbreak и root
MASVS-RESILIENCE-1 требует обнаружения компрометированных устройств. Стандартные проверки: наличие /Applications/Cydia.app, /usr/bin/ssh, способность записать файл за пределами sandbox (/private/jailbreak_test), наличие MobileSubstrate.
Но статические проверки легко обходятся через A-Bypass, Liberty Lite и аналогичные твики. Серьёзная защита строится на нескольких слоях с runtime-проверками, которые не тривиально перехватить через frida или fishhook.
Готовые решения: IOSSecuritySuite (iOS, open source), rootbeer (Android). Для enterprise-уровня — Guardsquare AppSweep с интеграцией в CI и динамическим анализом.
Что входит в работу по безопасности мобильного приложения
| Этап |
Что делаем |
Результат |
| Аудит по OWASP MASVS L1/L2 |
Анализ бинарника, трафика, исходников (если доступны) |
Отчёт с критичностью, рекомендации |
| Реализация pinning |
Настройка TrustKit / network_security_config, тест на продакшн-сертификате |
Защищённый канал без регрессий |
| Обфускация и R8/ProGuard-тюнинг |
Настройка правил, тесты на краши, интеграция SwiftShield/DexGuard |
Бинарник, трудночитаемый для jadx/class-dump |
| Jailbreak/root-детекция |
Установка IOSSecuritySuite / rootbeer + runtime-проверки |
Приложение блокируется на взломанных устройствах |
| Безопасное хранение |
Keychain (iOS) / EncryptedSharedPreferences+Keystore (Android) |
Токены и секреты не утекают даже при бэкапе |
| Поддержка и документация |
Интеграция в CI, обучение разработчиков |
Всё воспроизводится на новых версиях |
Как мы реализуем защиту: кейс
Один клиент пришёл с банковским приложением, которое не проходило аудит безопасности. Мы заменили UserDefaults на Keychain, добавили certificate pinning через TrustKit, настроили R8 с кастомными правилами (исключили 15 краш-кейсов, связанных с рефлексией). Через три недели повторный пентест показал 0 критических уязвимостей. С момента внедрения — ни одного инцидента за два года.
Сроки и стоимость
- Security-аудит по OWASP MASVS уровня L1 — от 1 до 2 недель.
- Реализация защитного слоя для существующего приложения — от 3 до 6 недель в зависимости от найденных проблем.
- Полный цикл «аудит + внедрение + тест» — от 4 до 8 недель.
Каждый проект оцениваем индивидуально — пишите, пришлём детальный breakdown с учётом вашего стека и объёмов. Работаем под ключ: от анализа до деплоя в сторах.
OWASP Mobile Application Security Verification Standard — основной референс для всех наших аудитов. Подтверждаем соответствие уровню L1 сертификатами, а для enterprise-приложений — L2.
Оценим ваш проект за один рабочий день после получения APK/IPA. Свяжитесь — расскажем, какие дыры закроем в первую очередь.