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 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 KB по запису. За велике количине користите шифровање датотека и чување кључа у Keychain-у, а саме податке — у Documents-у.
Након jailbreak-а, заштита Keychain-а се смањује, јер нападач добија root приступ и може директно читати Keychain SQLite. За критичне податке користите додатно шифровање са кључем добијеним са сервера.
Резиме
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође