Keychain (Ланац кључева) — заштићено складиште у iOS-у, намењено безбедном чувању лозинки, криптографских кључева, цертификата и поверљивих белешки. Према Apple Security Documentation (2025), Keychain користи хардверско шифровање кроз Secure Enclave на свим уређајима са чипом A7 и новијим. Разумевање архитектуре iOS Keychain је неопходно сваком програмеру за правилно чување токена и тајни апликације.
Главно
iOS Keychain — заштићени механизам за чување поверљивих података, уграђен у Apple оперативни систем. За разлику од UserDefaults-а или обичних датотека, Keychain шифрује све елементе на хардверском нивоу и пружа фину контролу приступа засновану на политикама сигурности.
Keychain је представљен у iOS 2.0 и од тада је прошао значајне промене: у iOS 7 додата је подршка за хардверске кључеве кроз Secure Enclave, у iOS 9 — подела Keychain-а између апликација кроз Access Groups, у iOS 13 — подршка биометријског повезивања кроз LAContext. Према Apple WWDC Session (2024), више од 90% iOS апликација у топ 100 App Store-а користи Keychain за чување токена за аутентификацију.
Архитектурно, Keychain је шифрована SQLite база података изван песканика апликације. Сваки елемент (SecItem) се шифрује посебним кључем, који је заштићен хардверским кључем Secure Enclave-а. Системска услуга Securityd управља приступом Keychain-у на основу права апликације (entitlements) и тражене класе заштите.
Важна предност Keychain-а у односу на друге начине чувања: подаци се аутоматски шифрују и дешифрују од стране оперативног система. Програмер не мора да ручно имплементира криптографију — довољно је позвати SecItemAdd са правилним параметрима. iOS гарантује да подаци из Keychain-а не могу бити прочитани од других апликација (уз правилну конфигурацију Access Groups).
Архитектура Keychain-а укључује више нивоа: физички (Secure Enclave), системски (Security.framework), апликативни (API SecItem*) и логички (Access Groups, Protection Classes). Разумевање сваког нивоа помаже у правилном пројектовању складиштених тајни.
Основни API за рад са Keychain-ом су функције Security framework-а: SecItemAdd за додавање, SecItemCopyMatching за читање, SecItemUpdate за ажурирање и SecItemDelete за брисање. Свака функција прима query речник који описује атрибуте траженог или сачуваног елемента.
Кључни атрибути query-а: kSecClass — тип елемента (kSecClassGenericPassword, kSecClassKey, kSecClassCertificate), kSecAttrAccount — јединствени идентификатор у оквиру класе, kSecValueData — сачувани подаци (Data), kSecAttrAccessible — класа заштите. SecItemCopyMatching са заставицом kSecReturnData враћа податке елемента, са kSecMatchLimit — број резултата.
Важно: све функције враћају OSStatus. Успешна операција враћа errSecSuccess (0). Грешке: errSecItemNotFound (-25300) — елеменат није пронађен, errSecDuplicateItem (-25299) — елеменат већ постоји, errSecAuthFailed (-25293) — биометријска аутентификација није успела. Програмер треба да правилно обрађује сваки статус.
Protection Class — атрибут kSecAttrAccessible, који одређује када су подаци у Keychain-у доступни за читање. iOS подржава шест класа заштите са различитим нивоом доступности и сигурности.
Препоручена класа за већину сценарија је kSecAttrAccessibleWhenUnlockedThisDeviceOnly: подаци су доступни само кад је уређај откључан и не копирају се у iCloud Backup. За податке који морају бити доступни након поновног покретања (али само након првог откључавања), користите kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly. За критичне податке који захтевају биометријску аутентификацију при сваком приступу, комбинујте kSecAttrAccessibleWhenUnlockedThisDeviceOnly са ACL-ом који захтева биометрију.
Класе без суфикса ThisDeviceOnly (kSecAttrAccessibleWhenUnlocked, kSecAttrAccessibleAfterFirstUnlock) дозвољавају копирање у iCloud Backup. То је згодно за корисника, али смањује сигурност — подаци се могу вратити из бекапа. За токене аутентификације увијек користите ThisDeviceOnly.
Access Control List (ACL) — механизам који ограничава операције над елементом Keychain-а на основу аутентификације корисника. ACL се поставља кроз SecAccessControlCreateWithFlags и прослеђује у атрибут kSecAttrAccessControl приликом чувања елемента.
Подржане заставице: kSecAccessControlUserPresence — било која аутентификација (Face ID, Touch ID или лозинка), kSecAccessControlBiometryCurrentSet — само биометрија (тренутно регистровани отисци или лице), kSecAccessControlDevicePasscode — само лозинка. ACL се примењује на сваку операцију: читање, ажурирање и брисање елемента такође захтева аутентификацију.
У iOS 15+ појавила се заставица kSecAccessControlWatch — за Apple Watch, која омогућава аутентификацију путем упареног сата. ACL се може комбиновати: на примјер, kSecAccessControlUserPresence или kSecAccessControlBiometryAny са опционалном лозинком (.or оријентација).
iOS Keychain подржава четири главне класе елемената (kSecClass), свака намењена за свој тип података. Правилан одабир класе поједностављује организацију и проналажење елемената.
kSecClassGenericPassword — општа лозинка: најчешће коришыена класа. Чува произвољне бинарне податке (Data) са јединственим кључем (kSecAttrAccount). Погодна за токене, API кључеве, PIN кодове. Не захтева додатна права (entitlements) за коришћење.
kSecClassInternetPassword — интернет лозинка: чува податке повезане са мрежним ресурсом. Додатни атрибути: kSecAttrServer (домен сервера), kSecAttrProtocol (https, ftp), kSecAttrPort, kSecAttrAuthenticationType. iOS може аутоматски попуњавати такве лозинке путем AutoFill-а.
kSecClassKey — криптографски кључ: за чување кључева за шифровање (AES, RSA, EC). Кључ се чува као SecKeyRef, а не као Data. kSecClassCertificate — X.509 цертификат за чување и провјеру дигиталних цертификата. Обе класе захтевају разумијевање криптографских операција и правилну конфигурацију атрибута.
У пракси, 95% случајева коришћења Keychain-а у мобилним апликацијама покривају kSecClassGenericPassword за чување токена аутентификације и kSecClassKey за чување приватних кључева за шифровање. kSecClassCertificate се користи ретко — обично у корпоративним апликацијама са сопственим PKI-јем.
Размотримо практичне примјере рада са Keychain-ом у Swift-у коришћењем Security framework-а. Сваки примјер укључује обраду грешака и правилну конфигурацију Protection Class-а.
Основни примјер чува токен аутентификације у Keychain-у са заштитом WhenUnlockedThisDeviceOnly. Кључ (kSecAttrAccount) је идентификатор услуге, подаци (kSecValueData) су токен у Data формату.
import Security
enum KeychainError: Error {
case unexpectedStatus(OSStatus)
}
func saveToken(token: String, service: String) throws {
let data = Data(token.utf8)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: "auth_token",
kSecValueData as String: data,
kSecAttrAccessible as String:
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.unexpectedStatus(status)
}
}
func readToken(service: String) throws -> String {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: "auth_token",
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(
query as CFDictionary, &result
)
guard status == errSecSuccess,
let data = result as? Data else {
throw KeychainError.unexpectedStatus(status)
}
return String(decoding: data, as: UTF8.self)
}
Примјер приказује коришћење SecAccessControlCreateWithFlags за повезивање кључа са биометријом. Сваки приступ елементу захтеваће Face ID или Touch ID.
import LocalAuthentication
func saveWithBiometry(data: Data, key: String) throws {
let accessControl = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.biometryCurrentSet,
nil
)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessControl as String: accessControl as Any
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.unexpectedStatus(status)
}
}
Примјер приказује поставку Access Group-а за заједнички приступ Keychain-у између апликација истог програмера. Захтева entitlement keychain-access-groups.
// Capabilities: Keychain Sharing је укључен
// App IDs: group.com.example.shared
func saveSharedToken(token: Data) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "shared_token",
kSecValueData as String: token,
kSecAttrAccessGroup as String:
"group.com.example.shared",
kSecAttrAccessible as String:
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemAdd(query as CFDictionary, nil)
}
Правилно коришћење iOS Keychain-а захтева поштовање неколико кључних правила која спречавају типичне рањивости и губитак података.
Користите ThisDeviceOnly за све тајне аутентификације: kSecAttrAccessibleWhenUnlockedThisDeviceOnly гарантује да токени неће доспјети у iCloud Backup. Ако нападач добије приступ бекапу, подаци Keychain-а са овом заставицом неће бити доступни. Изузетак су подаци који морају бити доступни на свим корисничким уређајима (на примјер, кључеви за шифровање за сопствене услуге), за које користите kSecAttrAccessibleWhenUnlocked са kSecAttrSynchronizable.
Не чувајте сирове лозинке — чувајте хешеве или токене сесија. Apple Security Guide (2025) препоручује да никада не чувате корисничку лозинку у Keychain-у у отвореном тексту. Уместо тога, чувајте refresh токен добијен од сервера након успешне аутентификације путем OAuth 2.0. Лозинка се користи само за добијање токена и одмах се брише из меморије.
Правилно обрађујте Keychain грешке: свака операција са Keychain-ом враћа OSStatus који треба провјерити. Посебну пажњу посветите errSecItemNotFound (токен је истекао или је обрисан) и errSecAuthFailed (биометрија није успела). У првом случају, апликација треба да захтева нову аутентификацију, у другом — да прикаже кориснику алтернативни начин (лозинка). Никада не игноришите errSecItemNotFound статус — то ће довести до пада апликације при покушају читања nil-а.
Тестирајте Keychain на реалном уређају: симулатор нема Secure Enclave и не подржава биометријске ACL-ове. Увијек провјеравајте сценарије: прво покретање, враћање из бекапа, промена лозинке уређаја, брисање и поновно инсталирање апликације. На реалном уређају, Keychain се чува након брисања апликације, али само ако није коришћена заставица kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, која се брише приликом уклањања лозинке.
Минимизирајте број операција са Keychain-ом: свака операција читања или писања је позив системској услузи Securityd, која може да блокира нит. Кеширајте прочитане токене у меморији током сесије и поново приступајте Keychain-у само при поновном покретању апликације или грешци аутентификације (401 од сервера). iOS аутоматски блокира Keychain приликом закључавања уређаја, зато планирајте читање кроз LAContext са захтевом за биометријом.
Често постављана питања
Да, користите класу заштите kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly или kSecAttrAccessibleAfterFirstUnlock. Подаци ће бити доступни након првог откључавања уређаја након поновног покретања. За аутоматски приступ при покретању апликације (без чекања откључавања) користите kSecAttrAccessibleAlways, али то смањује сигурност.
Позовите SecItemDelete са query-ом који садржи kSecClass за сваки тип података. За потпуно чишћење свих елемената апликације извршите: SecItemDelete([kSecClass as String: kSecClassGenericPassword] as CFDictionary). Поновите за kSecClassKey, kSecClassCertificate и kSecClassInternetPassword.
kSecAttrAccessible одређује када су подаци доступни (при откључавању, након првог откључавања итд.). kSecAttrAccessControl одређује ко може да приступи (биометрија, лозинка, било која аутентификација). Они се комбинују: прво Protection Class, затим ACL. На примјер, подаци су доступни само при откључавању И само након Face ID-а.
Разлози: елеменат никада није сачуван, елеменат је обрисан приликом уклањања лозинке (ако је коришћен kSecAttrAccessibleWhenPasscodeSet), апликација је поново инсталирана (Keychain се чува, али се не враћа из бекапа на новом уређају), промењена је Access Group или идентификатор програмерског тима. Провјерите kSecAttrService и kSecAttrAccount.
Користите LAContext из LocalAuthentication-а: позовите context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil). Ако враћа true — уређај подржава Touch ID или Face ID. За Keychain ACL користите заставицу biometryCurrentSet (само тренутни биометријски подаци) или biometryAny (било који раније регистровани).
Закључак
Развићемо мобилну апликацију под кључ
IT Sectr креира iOS и Android апликације за стартапе и предузећа од 2017. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође