Перейти к содержанию

Хранение sensitive-информации в KeyChain

Критичность: ИНФО
Способ обнаружения: DAST, SENSITIVE INFO

Описание

KeyChain — небольшое защищенное хранилище данных, которое предоставляет операционная система iOS. Фактически представляет собой SQLite-базу с зашифрованными данными (/private/var/Keychains/keychain-2.db) и является одним из рекомендуемых способов хранения конфиденциальной информации пользователя на устройстве. Несмотря на то, что KeyChain зашифровано, есть несколько способов получения его содержимого с устройства:

  1. Зашифрованный локальный бэкап устройства. При установке пароля при создании локальной резервной копии устройства в него также попадают и элементы KeyChain, которые хранятся на устройстве, в том числе и данные приложений.
  2. Jailbreak. При возможности осуществить процедуру Jailbreak благодаря специальным инструментам появляется возможность просмотреть и выгрузить все элементы KeyChain в открытом виде.
  3. Облачное хранилище KeyChain. Благодаря опции синхронизации элементов хранилища между несколькими устройствами, например между компьютером под управлением MacOS и устройством с iOS, данные попадают в облачные хранилища, а при компрометации последних — в руки злоумышленников.
  4. Данные KeyChain не очищаются после удаления приложения. При удалении приложения данные KeyChain сохраняются, в отличие от информации, находящейся в «песочнице» приложения (файловой системе). Если пользователь продаст устройство без сброса к заводским настройкам, покупатель может получить доступ к учетным записям приложений и данным предыдущего пользователя, просто установив приложение и посмотрев, что оно хранит в KeyChain (при условии, что разработчик не имплементировал очистку хранилища после переустановки приложения).

Именно поэтому не рекомендуется сохранять чувствительные данные пользователя в открытом виде в KeyChain, даже при условии его защищенности.

Рекомендации

Перед сохранением данных в KeyChain их рекомендуется шифровать или хэшировать в зависимости от способа дальнейшего использования и необходимости получения исходного значения. Но тогда остро встает вопрос, а где в таком случае хранить ключ шифрования. В iOS есть механизм под названием Security Enclave. Он представляет собой отдельную защищенную безопасную среду на устройстве — это отдельный «безопасный мир» со своим процессором и небольшой операционной системой (включая ядро, драйвера, сервисы и приложения), доступа к которой нет ни у кого в системе, даже у основного процессора. Этот механизм широко используется самой операционной системой для хранения данных TouchID, FaceID, Apple Pay и т. д.

Благодаря тому, что операционная система Apple работает только на устройствах одного производителя, можно быть уверенным, что при запуске на устройстве с определенной версией iOS будет предоставлена возможность хранения ключей в безопасном аппаратном хранилище. Достаточно обратиться к этому механизму, сгенерировать там ключи и зашифрованное значение поместить в Keychain.

В качестве примера использования рассмотрим интересную библиотеку EllipticCurveKeyPair. Первое, что необходимо сделать, это добавление зависимости в Сocoapods

pod EllipticCurveKeyPair

или в Carthage

github "agens-no/EllipticCurveKeyPair"

Далее создадим экземпляр менеджера, который в дальнейшем будет управлять ключами и служить интерфейсом для всех операций.

struct KeyPair {
    static let manager: EllipticCurveKeyPair.Manager = {
        let publicAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAlwaysThisDeviceOnly, flags: [])
        let privateAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, flags: [.userPresence, .privateKeyUsage])
        let config = EllipticCurveKeyPair.Config(
            publicLabel: "payment.sign.public",
            privateLabel: "payment.sign.private",
            operationPrompt: "Confirm payment",
            publicKeyAccessControl: publicAccessControl,
            privateKeyAccessControl: privateAccessControl,
            token: .secureEnclave)
        return EllipticCurveKeyPair.Manager(config: config)
    }()
}

Также, если по какой-то причине Secure Enclave недоступен, можно использовать KeyChain.

let privateAccessControl = EllipticCurveKeyPair.AccessControl(protection: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly, flags: {
    return EllipticCurveKeyPair.Device.hasSecureEnclave ? [.userPresence, .privateKeyUsage] : [.userPresence]
}())

Собственно, любые операции теперь можно совершать, используя KeyPair.manager.

Шифрование

do {
    let digest = "some text to encrypt".data(using: .utf8)!
    let encrypted = try KeyPair.manager.encrypt(digest, hash: .sha256)
} catch {
    // handle error
}

Что интересно, можно шифровать данные на другом устройстве/ОС/платформе с помощью открытого ключа. Если хотите узнать все подробности о том, как шифровать в формате, который понимает Secure Enclave, рекомендуем ознакомиться с этой статьей. Как вариант, можно дополнительно шифровать важные данные перед их отправкой на устройство.

Расшифровка

do {
    let encrypted = ...
    let decrypted = try KeyPair.manager.decrypt(encrypted, hash: .sha256)
    let decryptedString = String(data: decrypted, encoding: .utf8)
} catch {
    // handle error
}

Подпись

do {
    let digest = "some text to sign".data(using: .utf8)!
    let signature = try KeyPair.manager.sign(digest, hash: .sha256)
} catch {
    // handle error
}

Для подписи на закрытом ключе есть интересная идея использования — практически аналог двухфакторной аутентификации или один из вариантов усиления/дополнения этого механизма:

  1. Пользователь запрашивает соглашение, покупку или какую-либо операцию, которая требует его явного согласия.
  2. Сервер отправляет push-уведомление с токеном, который должен быть подписан.
  3. На устройстве подписываем токен при помощи закрытого ключа (что обязательно требует от пользователя подтверждения через FaceID/TouchID).
  4. Подписанный токен отправляется на сервер.
  5. Сервер при помощи открытого ключа проверяет подпись.
  6. Если все прошло успешно, можно быть уверенным, что пользователь действительно подтвердил покупку/соглашение с использованием биометрии.

Достаточно удобная библиотека, которая большинство функций берет на себя и предоставляет неплохие возможности.

Ссылки

  1. https://github.com/agens-no/EllipticCurveKeyPair
  2. https://darthnull.org/security/2018/05/31/secure-enclave-ecies/
  3. https://www.blackhat.com/docs/us-16/materials/us-16-Mandt-Demystifying-The-Secure-Enclave-Processor.pdf
К началу