Introduktion till säker datalagring på iOS
Skydda känsliga användardata är ett grundläggande ansvar för alla iOS-program. Oavsett om du lagrar autentiseringstokens, krypteringsnycklar eller privata referenser, tillhandahåller plattformen en dedikerad hårdvarubaserad lösning: ]Keychain ]]. Till skillnad från eller egendomslistafiler krypterar Keychain data i vila och genomdriver strikta åtkomstkontroller. Denna artikel ger en omfattande guide för att implementera säker datalagring med Keychain, som täcker både den inhemska säkerhetsramen och den praktiska produktionsprogramvaran.
Förstå iOS Keychain
Keychain är en säker lagringsbehållare som hanteras av operativsystemet. Den lagrar små, känsliga objekt - som lösenord, kryptografiska nycklar eller certifikat - i en krypterad databas. Data som skrivs till Keychain är skyddad även när enheten är låst. Key-funktioner inkluderar:
- Encryption i vila ] med hjälp av hårdvara-stödda AES-256.
- Access control ] via enhetspasskod, Touch ID eller Face ID.
- Persistence över app ominstallerar (om konfigurerad) och valfri iCloud-synkronisering.
- ]Isolering] mellan appar: som standard kan en app inte läsa en annan apps Keychain-objekt om de inte delar en Keychain-åtkomstgrupp.
Keychain är inte utformad för stora blobs; hålla varje objekt under några kilobyte. För större data, överväga att använda ] API eller ] ramen tillsammans med filbaserad kryptering.
Keychain Services API vs. Tredjepartsbibliotek
Apple ger infödda ]Keychain Services ] API (C-baserade, ), som är kraftfull men verbose. Du kan använda den direkt, eller anta en swift-vänlig wrapper. Popular tredjepartsbibliotek som ]]]KeychainAccess ]] eller ]]]] reducerar pant.
Ställa in Keychain Storage
Innan du lagrar någonting måste du bestämma om ]Keychain-objektklassen. Den vanligaste för generiska lösenord är ]]. För Internetlösenord eller certifikat finns det andra klasser. Varje objekt refereras av en uppsättning attribut - en ordbok (CFDictionary) som beskriver objektet.
Grundflödet följer alltid detta mönster:
- Bygg en frågeordbok med objektklassen och attributen.
- ] [[[]]]] []]]]] []]]]]]]]]]] [[]]]]]]]]]]]]]]
- Kontrollera den returnerade []] eller en felkod.
Innan du skriver kod, importera säkerhetsmodulen:
import Security
import Foundation // for Data and String utilities
Lagra data i Keychain
Skriva ett generiskt lösenord
För att spara en token (t.ex. en JWT) för den aktuella användaren:
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
}
Nyckelpunkter:
- fungerar som en primärnyckel; välj en unik sträng (t.ex. användar-ID eller en konstant som ).
- ] kontrollerar när objektet kan läsas. Använd ]] för bästa säkerhet; det förhindrar iCloud-backup och begränsar åtkomsten till den aktuella enheten.
- Vi kallar innan du lägger till för att undvika att samla dubbla objekt. Alternativt kan du använda .
Lägga till Access Control (Biometry eller lösenord)
För högkänsliga data, kräver Touch ID eller Face ID innan du läser:
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)
Nu kommer alla ] kräver att detta objekt kommer att utlösa en biometrisk eller lösenkod snabb. Använd ] från LocalAuthentication för att hantera användarinteraktionen graciöst.
Hämta data från Keychain
För att läsa den lagrade token:
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
}
] till ]] för att få tillbaka data. Använd för att hämta ett enda resultat. Om du utelämnar gränsen kan API returnera en array.
]: []]] När du använder åtkomstkontroll (biometri), ]]]] kan samtalet returnera ]] om användaren avbryter. Hantera detta fall separat och aldrig falla tillbaka till vanlig textlagring.
Uppdatering och radering av Keychain-objekt
Uppdatera ett befintligt objekt
Istället för att ta bort och återskapa, använd :
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
}
Detta är mer effektivt än en rad + annons, och det undviker potentiella race villkor.
Ta bort ett objekt
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
}
Var noga med att inte ta bort objekt som hör till andra appar som delar samma åtkomstgrupp - om du alltid använder delade Keychains.
Access Control och Accessibility Attribuerar
konstant definierar ] när ]] Keychain-objektet kan läsas. Välj det mest restriktiva alternativet som fortfarande uppfyller din apps behov:
| 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. |
För de flesta appar, ] slår rätt balans mellan säkerhet och användbarhet. Om du behöver läsa objekt i bakgrunden (t.ex. en bakgrundsuppdateringstoken), måste du använda (och acceptera att data är något mindre skyddad).
Felhantering och gemensamma fallgropar
] funktioner returnera en ]. Kontrollera alltid det och hantera fel på lämpligt sätt. Vanliga fel:
- (-25300) - Inget objekt matchar frågan.
- (-25299) - Ett objekt med samma primärnyckel finns redan (om du inte tog bort först).
- (-128) - Användare avbruten biometrisk snabbhet.
- (-25293) - Autentisering misslyckades eller biometri inte tillgänglig.
Aldrig ignorera en icke-framgångsstatus. Gracefully degrade: visa ett felmeddelande eller förfall, men aldrig lagra känsliga data utanför Keychain som en återgång. Du kan använda för att kontrollera biometrisk tillgänglighet innan du försöker få tillgång till det.
Bästa praxis och produktionsövervägningar
- Använd unika, beskrivande kontonamn per användare eller per objekttyp för att undvika kollisioner.
- ] Anger alltid en tillgänglighetsattribut ; annars gäller systemets standard () som kanske inte är idealiskt.
- ] Rensa Keychain-data när användaren loggar ut - läs igenom alla kända konton och ta bort objekt.
- Använd Keychain Access-grupper ]] endast när du delar mellan dina egna appar. Undvik breda grupper.
- ]]] ] [[[[[]]]] (som användarinställningar) i Keychain-använd ]]]]]] eller en databas istället.
- ] Tänk på att använda ] med [[]] för avancerade scenarier (macOS Catalyst).
- ]Test på en riktig enhet ]; Simulatorn använder en programvara Keychain som beter sig annorlunda från hårdvarubaserad lagring.
Använda Keychain med SwiftUI och Async/Await
För moderna appar, wrap Keychain operationer i en skådespelare eller en async-säker klass för att undvika att blockera huvudtråden. Exempel med :
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)
}
}
Om du använder biometri kan ]-samtal blockera tråden medan du väntar på användarinteraktion. Släpp det i en bakgrundskö eller bättre, använd ]]s ]]-metod före Keychain-samtalet.
Slutsats
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.