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 допълнително синхронизира данни между устройства чрез end-to-end криптиране.
Според 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 резервно копие, откъдето могат да бъдат извлечени с инструменти за анализ. За критични данни комбинирайте достъпността с 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 KB на запис. За големи обеми използвайте криптиране на файлове и съхранение на ключа в Keychain, а самите данни — в Documents.
При джейлбрейк защитата на Keychain намалява, тъй като нападателят получава root достъп и може директно да чете Keychain SQLite. За критични данни използвайте допълнително криптиране с ключ, получен от сървъра.
Резюме
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също