Keychain — це захищене сховище облікових даних у iOS, watchOS, macOS і tvOS, яке керує паролями, ключами та сертифікатами на апаратному рівні. На відміну від UserDefaults, дані в Keychain шифруються та ізолюються за пісочницею додатка. За даними Apple Developer Documentation, Keychain використовує механізми Secure Enclave та апаратне шифрування AES-256 для захисту конфіденційної інформації.
Головне
Keychain — це захищена база даних операційної системи Apple, призначена для безпечного зберігання паролів, криптографічних ключів, сертифікатів та інших конфіденційних даних. Вперше Keychain з'явився в Mac OS 8.6 у 1999 році, а на iOS — з найпершої версії SDK.
Keychain вирішує фундаментальну проблему мобільної розробки: зберігання секретів (токенів, паролів, ключів шифрування) у незахищеній файловій системі. На відміну від UserDefaults або SQLite, дані в Keychain зберігаються в зашифрованому вигляді й автоматично шифруються під час запису на диск.
Кожен додаток iOS працює в ізольованій пісочниці та має доступ лише до свого keychain. Однак розробник може налаштувати спільний доступ до Keychain між додатками одного розробника через keychain access groups. Користувацький iCloud Keychain додатково синхронізує дані між пристроями через наскрізне шифрування.
За даними Apple Security White Paper (2025), Keychain використовує апаратне шифрування AES-256 з ключем, прив'язаним до унікального ідентифікатора пристрою (UID), що робить дані недоступними при вилученні з пристрою.
Keychain функціонує як сервіс рівнів: додаток звертається до Security framework, який взаємодіє з демоном securityd, що керує зашифрованою SQLite-базою Keychain. Кожен запис шифрується окремим ключем, а самі ключі захищені апаратним модулем Secure Enclave на пристроях з чипом A7 і новіше.
Security framework надає C-інтерфейс для роботи з Keychain Services. Основні функції: SecItemAdd (додавання), SecItemCopyMatching (пошук), SecItemUpdate (оновлення) і SecItemDelete (видалення). Кожна операція приймає словник атрибутів CFDictionaryRef, що описує тип запиту.
Дані класифікуються за класами: kSecClassGenericPassword (довільний пароль), kSecClassInternetPassword (пароль до інтернет-сервісу), kSecClassCertificate (сертифікат), kSecClassKey (криптографічний ключ) і kSecClassIdentity (зв'язка ключа та сертифіката).
При збереженні пароля додаток формує словник з атрибутами: сервіс, обліковий запис, рівень доступу, мітка. Security framework передає дані демону securityd, який шифрує запис ключем, прив'язаним до пристрою, і зберігає в Keychain SQLite. Витяг відбувається аналогічно: запит за атрибутами, пошук, розшифрування та повернення даних.
Важлива особливість: при видаленні додатка iOS автоматично очищає Keychain записи, створені цим додатком (з iOS 10.3+). При перевстановленні додатка дані Keychain попередньої інсталяції недоступні, якщо не використовувався iCloud Keychain.
import Security
func savePassword(service: String, account: String, password: Data) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecValueData as String: password,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlocked
]
SecItemAdd(query as CFDictionary, nil)
}
Keychain підтримує п'ять основних класів даних, кожен з яких має свій набір атрибутів і область застосування. Від вибору класу залежить, які ключі пошуку доступні та як дані шифруються.
Класи kSecClassGenericPassword і kSecClassInternetPassword призначені для зберігання рядкових паролів. Інтернет-версія додатково зберігає дані сервера: домен, протокол, порт і шлях автентифікації. GenericPassword використовується для зберігання будь-яких паролів без прив'язки до сервера.
При збереженні інтернет-пароля можна вказати kSecAttrServer (домен), kSecAttrProtocol (HTTPS, FTP тощо), kSecAttrPort і kSecAttrPath. Це дозволяє точно ідентифікувати запис при автоматичному заповненні через AutoFill.
Клас kSecClassKey зберігає симетричні (AES) і асиметричні (RSA, EC) ключі. Клас kSecClassCertificate — сертифікати X.509. Клас kSecClassIdentity об'єднує закритий ключ і відповідний сертифікат в один запис.
Для асиметричних ключів Keychain підтримує атрибути: kSecAttrKeyType (RSA, EC), kSecAttrKeySizeInBits (2048, 256), kSecAttrIsPermanent (чи зберігати в Keychain). Для сертифікатів: kSecAttrLabel (людиночитане ім'я), kSecAttrSubject (DN власника), kSecAttrIssuer (DN видавця).
| Клас Keychain | Призначення | Тип даних |
|---|---|---|
| kSecClassGenericPassword | Пароль додатка | Data (NSString) |
| kSecClassInternetPassword | Пароль до сервера | Data + атрибути сервера |
| kSecClassCertificate | Сертифікат X.509 | SecCertificate |
| kSecClassKey | Криптографічний ключ | SecKey |
| kSecClassIdentity | Ключ + сертифікат | SecIdentity |
Основний інтерфейс взаємодії з Keychain у нативному коді iOS — функції Security framework. Для Swift і Objective-C доступний один C-інтерфейс, обгорнутий у зручні функції.
Функція SecItemAdd додає новий елемент у Keychain. На вхід приймає словник із класом, даними та атрибутами. Повертає статус errSecSuccess (0) або код помилки. Функція SecItemCopyMatching виконує пошук за атрибутами та повертає знайдені дані.
Для пошуку використовується прапорець kSecReturnData — якщо true, функція повертає вміст запису. Прапорець kSecMatchLimit визначає кількість результатів: kSecMatchLimitOne (один) або kSecMatchLimitAll (усі).
func readPassword(service: String, account: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var item: CFTypeRef?
let status = SecItemCopyMatching(query as CFDictionary, &item)
guard status == errSecSuccess else { return nil }
return item as? Data
}
SecItemUpdate дозволяє змінити атрибути або дані існуючого запису. SecItemDelete видаляє всі записи, що відповідають переданому словнику. При видаленні важливо вказати достатню кількість атрибутів, щоб випадково не видалити чужі записи в shared keychain.
func deletePassword(service: String, account: String) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account
]
SecItemDelete(query as CFDictionary)
}
Кожен запис Keychain має атрибут kSecAttrAccessible, який визначає, коли дані доступні для читання. Це критично важливо для безпеки: неправильний рівень доступу може зробити дані вразливими або, навпаки, недоступними в потрібний момент.
kSecAttrAccessibleWhenUnlocked — дані доступні лише коли пристрій розблоковано. kSecAttrAccessibleAfterFirstUnlock — доступні після першого розблокування після перезавантаження. kSecAttrAccessibleAlways — доступні завжди (не рекомендовано). Постфікс ThisDeviceOnly забороняє перенесення даних через резервне копіювання.
На пристроях з Secure Enclave ключ шифрування Keychain може бути додатково захищений біометрією: kSecAccessControlBiometryCurrentSet або kSecAccessControlUserPresence. Це додає вимогу Face ID або Touch ID для кожної операції читання.
iCloud Keychain синхронізує записи між пристроями Apple через наскрізне шифрування (end-to-end). Apple не має доступу до розшифрованих даних. Для увімкнення синхронізації додайте атрибут kSecAttrSynchronizable зі значенням kCFBooleanTrue.
Важливо: синхронізовані записи не можуть містити атрибут ThisDeviceOnly, оскільки вони мають бути доступні на інших пристроях. При втраті всіх пристроїв iCloud Keychain відновлюється через код відновлення або резервний ключ iCloud.
Для токенів автентифікації використовуйте kSecAttrAccessibleWhenUnlockedThisDeviceOnly — дані доступні лише при розблокованому пристрої та не копіюються в бекап. Для сертифікатів, які мають бути доступні фоновим завданням, використовуйте kSecAttrAccessibleAfterFirstUnlock. Ніколи не використовуйте kSecAttrAccessibleAlways для конфіденційних даних.
Рекомендується завжди додавати постфікс ThisDeviceOnly для даних, що не потребують синхронізації. Це запобігає випадковому копіюванню секретів у iCloud або iTunes backup, де вони можуть бути вилучені інструментами аналізу. Для критичних даних комбінуйте доступність з kSecAccessControlUserPresence — це додає вимогу біометрії або коду для розшифрування запису.
При використанні iCloud Keychain враховуйте, що синхронізовані записи доступні на всіх пристроях користувача: якщо зловмисник отримає доступ до одного пристрою, дані можуть бути скомпрометовані на всіх. Для ізольованих секретів використовуйте ThisDeviceOnly.
Часті запитання
UserDefaults зберігає дані у відкритому вигляді у файлі .plist, доступному при аналізі IPA. Keychain шифрує кожен запис окремо, використовує апаратне шифрування та підтримує рівні доступу, біометрію та синхронізацію через iCloud.
При кожному запуску на симуляторі iOS Keychain очищається. На реальному пристрої видаліть додаток — iOS видалить всі Keychain-записи цього розробника. Для вибіркового очищення викличте SecItemDelete з відповідними атрибутами.
Так, через keychain access groups. Додатки повинні мати однаковий team ID у підписі та entitlements з однаковим keychain-access-groups. Розробник налаштовує спільний доступ у Xcode Capabilities.
Apple не документує строгий ліміт, але на практиці рекомендується не перевищувати 1–4 КБ на запис. Для великих обсягів використовуйте шифрування файлів і зберігання ключа в Keychain, а самі дані — у Documents.
При джейлбрейку захист Keychain знижується, оскільки зловмисник отримує root-доступ і може читати Keychain SQLite напряму. Для критичних даних використовуйте додаткове шифрування з ключем, отриманим із сервера.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також