Основная задача шифрования — обеспечить дополнительный уровень защиты данных пользователя. Однако почти во всех мобильных приложениях криптография используется неправильно. Это касается как самого приложения, так и сторонних SDK. Разработчики часто не понимают, для чего используется криптография, откуда брать ключи шифрования и что с ними делать, а также не знают угрозы, которые нужно учитывать. В этой статье я расскажу о самых распространенных ошибках и дам несколько практических советов по защите мобильных приложений.
Зачем шифрование в приложениях
И Android и iOS, предлагают безопасное хранилище, изолированное от сторонних приложений, в котором можно хранить чувствительные данные. Никто, кроме самого приложения (и root-пользователя), не сможет получить доступ к этим файлам. Следовательно, данные нужно защитить и от самого приложения. Если взглянуть на список типичных уязвимостей, можно увидеть множество примеров доступа к внутренним файлам приложения, например:
- через WebView в приложении Amazon,
- через доступ к произвольным контент-провайдерам,
- через неявные intent’ы,
- и сотни других примеров, не описанных в наших статьях.
Если бы данные в этих случаях были зашифрованы на уровне приложения, злоумышленник получил бы гораздо меньше пользы от этих уязвимостей. Чтение файлов из внутренних директорий привело бы лишь к утечке их зашифрованного содержимого. Перезапись этих файлов могла бы вызвать автоматический выход из аккаунта пользователя, но не более того.
Как не нужно шифровать данные
Часто ключи шифрования, которые защищают данные или интернет-коммуникации, жестко прописаны в коде. Пример такой уязвимости в уязвимом Android-приложении. Вот как выглядит результат анализа сегмента кода.

Пример такой уязвимости в уязвимом iOS-приложении.


Злоумышленник может декомпилировать приложение, понять алгоритм и вычислить ключ, а затем снять уровень защиты, что позволит расшифровать данные.
Одна из распространенных ошибок — использование предсказуемых значений для генерации ключей шифрования:
1 2 3 4 |
SecureRandom secureRandom = new SecureRandom("known_seed".getBytes()); KeyGenerator generator = KeyGenerator.getInstance("AES"); generator.init(256, secureRandom); Key key = generator.generateKey(); |
Если используются псевдослучайные значения:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
private byte[] generateKey() { byte[] key = new byte[128]; Random random = new Random(); for (int i = 0; i < 128; i++) { key[i] = (byte) random.nextInt(256); } return key; } private byte[] encrypt(byte[] data) { SecretKeySpec skeySpec = new SecretKeySpec(generateKey(), "AES"); //... } |
Если значение ключа не инициализировано, что автоматически приводит к ключу, состоящему из одних нулей:
1 2 3 4 5 |
private byte[] encrypt(byte[] data) { byte[] key = new byte[128]; SecretKeySpec skeySpec = new SecretKeySpec(key, "AES"); //... } |
Или если ключ генерируется безопасно, но затем хранится в открытом виде в файловой системе (часто в Shared Preferences).
Правильный способ шифрования на Android
Android предоставляет интерфейс Keystore. Приложения могут получать ключи шифрования только через программный интерфейс, а не напрямую из файлов. Сами файлы с ключами хранятся в директории /data/misc/keystore/ и принадлежат пользователю keystore.
Правильный способ шифрования на iOS
iOS предоставляет механизм Secure Enclave для работы с криптографическими ключами. Как и в Android, взаимодействие с этими ключами возможно только через программные интерфейсы — доступ к файлам невозможен.
Ты также можешь сам генерировать ключи и хранить их в Keychain.
Рекомендации по использованию шифрования
Если Android-приложения шифруют все чувствительные данные с помощью ключей из Keystore, а iOS-приложения используют ключи, хранящиеся в Keychain, расшифровать данные станет невозможно, если только не будут найдены уязвимости, позволяющие выполнять произвольный код. Произвольное выполнение кода — критическая, но сравнительно редкая уязвимость. Зато другие, менее серьезные уязвимости могут свести риск утечки данных в файловой системе к нулю.
По данным Secured, более 77% приложений содержат ошибки, связанные с жестко закодированными или предсказуемыми ключами шифрования, а также с неправильной конфигурацией алгоритмов шифрования (например, использование слабых шифров или небольших ключей).
Я рекомендую шифровать любые чувствительные данные, которые приложение хранит. Это включает аутентификационные токены пользователей (учетные данные пользователей никогда не должны храниться на клиенте!), финансовые и медицинские данные, а также личные сообщения и коммуникации пользователей.
Важно также удалять все данные пользователя и уничтожать ключи шифрования после выхода пользователя из системы.