Хранение sensitive-информации в приватном файле
Критичность: ИНФО | |
Способ обнаружения: DAST, ФАЙЛЫ ПРИЛОЖЕНИЯ |
Описание
Приложение хранит чувствительную информацию в приватном файле внутри директории приложения.
Чтобы понять, какие именно данные необходимо защищать, прежде всего необходимо определить, какие данные обрабатывает и хранит приложение и какая часть этой информации считается конфиденциальной. Как правило, в таких случаях полагаются на законодательство и здравый смысл. Нет смысла защищать шифрованием абсолютно всю информацию, которую хранит приложение — это может повлиять на скорость и стабильность работы. Вместо этого следует однозначно определить, какие именно данные для приложения или компании являются конфиденциальными, и сосредоточить свое внимание именно на этих данных.
Принято считать, что необходимо хранить как можно меньше конфиденциальных данных в локальном хранилище (внутреннем или внешнем). Однако в большинстве случаев хранения такой информации избежать не удастся. Например, с точки зрения удобства использования не стоит заставлять пользователя вводить сложный пароль при каждом запуске приложения. Большинство приложений должны локально кэшировать какой-либо токен аутентификации. Персонально идентифицируемая информация (PII) и другие типы конфиденциальных данных также могут быть сохранены, если этого требует конкретный сценарий.
Приложение может хранить данные в различных форматах, в базах данных, кэшированных сетевых запросах и много где еще.
Значение из внутренней директории приложения может быть получено через локальный или облачный бекапы, а также при помощи эксплуатации различных уязвимостей.
Внимание!
Очень часто ошибочно считается, что данные, которые хранятся во внутренней директории приложения, уже защищены при помощи механизма песочницы и злоумышленник до них не доберется. Существует большое количество способов, начиная от простого локального или облачного бекапа приложения и заканчивая физическим доступом к устройству и эксплуатации различных уязвимостей. Информация, размещенная в открытом виде внутри директории приложения, не защищена!
Рекомендации
Любую чувствительную информацию, которая хранится на устройстве, необходимо шифровать. Это можно сделать самыми разными способами и один из таких способов — это шифрование на основе ключей, которые генерируются в защищенном хранилище Security Enclave. Apple приложила много усилий, чтобы сделать процедуру шифрования проще и удобнее.
Процесс создания ключей в Security Enclave:
-
Шаги для создания приватного ключа в Secure Enclave (и соответствующего публичного ключа) практически аналогичны созданию ключа в обычной ситуации:
let access = SecAccessControlCreateWithFlags(kCFAllocatorDefault, kSecAttrAccessibleWhenUnlockedThisDeviceOnly, .privateKeyUsage, nil)! // Ignore error
-
Используя объект access control, создадим словарь:
let attributes: [String: Any] = [ kSecAttrKeyType as String: type, kSecAttrKeySizeInBits as String: 256, kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave, kSecPrivateKeyAttrs as String: [ kSecAttrIsPermanent as String: true, kSecAttrApplicationTag as String: <# a tag #>, kSecAttrAccessControl as String: access ] ]
-
Теперь, когда у нас есть словарь, создадим ключевую пару аналогично тому, как это делается за пределами Security Enclave — вызвав функцию SecKeyCreateRandomKey():
var error: Unmanaged<CFError>? guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else { throw error!.takeRetainedValue() as Error }
-
Теперь созданные ключи можно использовать для шифрования или подписи данных. Но применять можно только эллиптические алгоритмы, так как Security Enclave поддерживает лишь эллиптические ключи:
var error: Unmanaged<CFError>? guard let cipherText = SecKeyCreateEncryptedData(publicKey, algorithm, plainText as CFData, &error) as Data? else { throw error!.takeRetainedValue() as Error }
Такой порядок создания и использования описан в официальной документации, ну или можно использовать обвязки для упрощения всех процедур, как вариант — библиотеку EllipticCurveKeyPair.
В дополнение к этому можно применять алгоритмы, которые вообще не требуют хранения ключа, а создают его «на лету» из некоторых данных пользователя (например, его пароля или пин-кода). Такие алгоритмы называют процедурой расширения ключа. Они позволяют получить из небольшого количества информации длинный и хороший ключ для шифрования. Например, этот механизм можно использовать в подходе KEK&DEK (Key Encryption Key & Data Encryption Key). Такой подход проще всего показать на блок-схеме:
При этом подходе мы сначала создаем ключ для шифрования данных (Data Encryption Key), затем на нем зашифровываем данные и уже этот ключ шифруем при помощи нового ключа (Key Encryption Key). Как раз этот KEK можно либо сохранить в Keystore/Security Enclave, либо генерировать каждый раз (например, на основе пароля пользователя). При таком механизме мы избавляемся от перешифровки данных в случае изменения/компрометации ключа. Нам достаточно перешифровать только DEK и не трогать данные (конечно, это не так, если у нас скомпрометирован DEK, но такой вариант маловероятен). В этом случае, каков бы ни был объем данных, которые нужно сохранить в секрете, время перешифрования всегда будет одинаковым, так как сами зашифрованные данные мы не трогаем. Кстати, такой подход используется в iPhone для шифрования данных в файловой системе (там, конечно все еще сложнее, но принцип именно такой).
Ссылки
- https://developer.apple.com/documentation/security/keychain_services
- https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/storing_keys_in_the_secure_enclave
- https://developer.apple.com/documentation/security/certificate_key_and_trust_services/keys/using_keys_for_encryption
- https://github.com/agens-no/EllipticCurveKeyPair