Table of Contents
Introducere în stocarea securizată a datelor pe iOS
Protejarea datelor sensibile ale utilizatorilor este o responsabilitate fundamentală a oricărei aplicații iOS. Fie că stocați jetoane de autentificare, chei de criptare sau legitimații private, platforma oferă o soluție dedicată cu suport hardware: Keychain. Spre deosebire de sau fișiere de listă de proprietăți, Keychain criptează datele în repaus și aplică controale de acces stricte.Acest articol oferă un ghid cuprinzător pentru implementarea stocării securizate a datelor cu Keychain, acoperind atât cadrul nativ de securitate, cât și cele mai bune practici practice pentru aplicațiile de producție.
Înțelegerea cheii iOS
Keychain este un container de stocare securizat gestionat de sistemul de operare. Acesta stochează articole mici, sensibile . Cum ar fi parole, chei de acces, sau certificate într-o bază de date criptate. Datele scrise la Keychain este protejat chiar și atunci când dispozitivul este blocat. Capacitățile cheie includ:
- Criptare în repaus folosind AES-256 suport hardware.
- Controlul de acces prin codul de acces al dispozitivului, ID-ul tactil sau ID-ul feței.
- Persistența în cadrul reinstalațiilor aplicației (dacă sunt configurate) și sincronizarea iCloud opțională.
- Isolație între aplicații: în mod implicit, o aplicație nu poate citi o altă aplicație; elementele Keychain decât dacă au un grup de acces la Keychain.
Keychain nu este proiectat pentru pete mari; păstrați fiecare element sub câteva kilobytes. Pentru date mai mari, ia în considerare utilizarea API sau cadru împreună cu criptarea pe bază de fișiere.
Keychain Services API vs. Bibliotecile Terţei părţi
Apple oferă nativ Keychain Services API (C-based, , care este puternic, dar verbose. Puteți să-l utilizați direct, sau să adopte un ambalaj Swift-friendly. Biblioteci populare terțe părți, cum ar fi [ ]KeychainAccess sau SwiftKeychainWrapper reduce placa cazanului. Cu toate acestea, înțelegerea API-ului de bază este esențială pentru depanare și atunci când aveți nevoie de control fin-grained asupra politicilor de acces. Astăzi ne vom concentra pe API nativ cu Swift.
Configurare stocare keychain
Înainte de a stoca orice, trebuie să decidă asupra Clasa element Keychain. Cel mai comun pentru parole generice este . Pentru parole sau certificate de Internet, există alte clase. Fiecare element este menționat printr-un set de atribute (CFDictionary) care descrie elementul.
Fluxul de bază întotdeauna urmează acest model:
- Construiește un dicționar de interogare cu clasa de element și atribute.
- Apelează funcția corespunzătoare [, , , .
- Verificați codul de eroare returnat [] sau codul de eroare].
Înainte de a scrie codul, importați modulul de securitate:
import Security
import Foundation // for Data and String utilities
Păstrarea datelor în lanţul cheilor
Scrierea unei parole generice
Pentru a salva un simbol (de exemplu, un JWT) pentru utilizatorul curent:
func saveToken(_ token: String, forAccount account: String) -> Bool {
guard let tokenData = token.data(using: .utf8) else { return false }
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: tokenData,
// Optional: restrict access to when device is unlocked
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
// Delete any existing item first to avoid duplicates
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
return status == errSecSuccess
}
Puncte cheie:
- acționează ca o cheie primară; alege un șir unic (de exemplu, ID-ul utilizatorului sau o constantă ca .
- ] controale atunci când elementul poate fi citit. Utilizați pentru cea mai bună securitate; previne backup iCloud și restrânge accesul la dispozitivul curent.
- Noi numim înainte de a adăuga pentru a evita acumularea de elemente duplicate. Alternativ, puteți utiliza .
Adăugare control acces (biometrie sau cod de acces)
Pentru date extrem de sensibile, trebuie să se solicite identificarea tactilului sau identificarea feței înainte de a se citi:
let accessControl = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.userPresence, // requires passcode, Face ID, or Touch ID
nil
)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: tokenData,
kSecAttrAccessControl as String: accessControl as Any
]
SecItemAdd(query as CFDictionary, nil)
Acum, orice apel pentru acest articol va declanșa o promptă biometrică sau parolă. Utilizați ] de la Autentificare Locală pentru a gestiona interacțiunea cu utilizatorul cu grație.
Obținerea datelor din lanțul de chei
Pentru a citi simbolul stocat:
func retrieveToken(forAccount account: String) -> String? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
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,
let data = item as? Data,
let token = String(data: data, encoding: .utf8) else {
return nil
}
return token
}
Setează la pentru a obține datele înapoi. Utilizați ] pentru a recupera un singur rezultat. Dacă omiteți limita, API-ul poate returna un array.
Important: Atunci când se utilizează controlul accesului (biometrie), apelul ar putea reveni dacă utilizatorul anulează. Se ocupă de acest caz separat și nu se poate retrage niciodată la stocarea simplă a textului.
Actualizarea și eliminarea elementelor de breloc
Actualizarea unui element existent
În loc să se elimine și să se readuleze, utilizați :
func updateToken(_ newToken: String, forAccount account: String) -> Bool {
guard let newData = newToken.data(using: .utf8) else { return false }
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account
]
let attributesToUpdate: [String: Any] = [
kSecValueData as String: newData
]
let status = SecItemUpdate(query as CFDictionary, attributesToUpdate as CFDictionary)
return status == errSecSuccess
}
Acest lucru este mai eficient decât o ștergere + add, și evită condițiile de cursa potențiale.
Ștergerea unui element
func deleteItem(forAccount account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account
]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
}
Fiți atenți să nu ștergeți elementele care aparțin altor aplicații care partajează același grup de acces.
Atribute privind controlul accesului și accesibilitatea
constant definește atunci când [ elementul Keychain poate fi citit. Alegeți opțiunea cea mai restrictivă care răspunde în continuare nevoilor aplicației dumneavoastră:
| Attribute | Meaning |
|---|---|
kSecAttrAccessibleWhenUnlocked | Available only while device is unlocked (default). |
kSecAttrAccessibleAfterFirstUnlock | Available after device boots and is unlocked once. Allows background access. |
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly | Requires a passcode to be set. Strictest option—prevents access even after unlock if passcode is removed. |
kSecAttrAccessibleWhenUnlockedThisDeviceOnly | Same as WhenUnlocked but does not back up to iCloud, and cannot be restored to another device. |
Pentru majoritatea aplicațiilor, atinge echilibrul corect între securitate și utilizare. Dacă trebuie să citiți elemente din fundal (de exemplu, un jeton de reîmprospătare a fundalului), trebuie să utilizați (și să acceptați că datele sunt puțin mai puțin protejate).
Manipularea erorilor și capcanele comune
Funcţiile returnează o . Verificaţi întotdeauna şi gestionaţi în mod corespunzător eşecurile.
- (
- (
- (
- (
Nu ignorați niciodată un statut non-succes. Degradați cu grație: arătați un mesaj de eroare sau rejudecați, dar niciodată nu stocați date sensibile în afara Keychain-ului ca o rezervă. Puteți utiliza pentru a verifica disponibilitatea biometrică înainte de a încerca accesul.
Cele mai bune practici și considerații privind producția
- Folosiţi nume de cont unice descriptive pe utilizator sau pe tip de element pentru a evita coliziunile.
- Se specifică întotdeauna un atribut de accesibilitate; altfel, se aplică sistemul implicit ([], care poate să nu fie ideal.
- Clear Keychain data when the user logs out]
- Folosiți grupuri de acces la breloc numai atunci când partajați între propriile aplicații. Evitați grupurile largi.
- Niciodată nu stoca date nesensibile (ca preferințele utilizatorilor) în utilizarea Keychain sau o bază de date în schimb.
- Consider care utilizează cu pentru scenarii avansate (catalizator MacOS).
- Test pe un dispozitiv real; Simulatorul folosește un software Keychain care se comportă diferit de stocarea cu suport hardware.
Folosind Keychain cu SwiftUI și Async/Await
Pentru aplicații moderne, înfășurați operațiile Keychain într-un actor sau o clasă de siguranță asinc pentru a evita blocarea firului principal. Exemplu folosind :
actor KeychainManager {
func saveToken(_ token: String, for account: String) async -> Bool {
// same implementation as above, but now it's safe to call from any context
return saveToken(token, forAccount: account)
}
}
Dacă utilizați biometrice, ] apelul poate bloca firul în timp ce așteaptă interacțiunea cu utilizatorul. Înfășurați-l într-o coadă de fundal, sau mai bine, utilizați ] ] metoda înainte de apelul Keychain.
Concluzie
The iOS Keychain is the correct place to store small, sensitive pieces of data. By using the native Keychain Services API, you gain direct control over encryption, accessibility, and authentication policies. Always pair your Keychain usage with solid error handling and remember to clear data when appropriate. For further reading, refer to the Apple Keychain Service Documentation and the Keychain Concepts overview. Adopting these practices will help you ship iOS apps that respect user privacy and withstand security scrutiny.