Introduksjon til sikker datalagring på iOS

Beskytte sensitive brukerdata er et grunnleggende ansvar for ethvert iOS-program. Enten du lagrer autentiseringssymboler, krypteringsnøkler eller private legitimasjoner, gir plattformen en dedikert maskinvare-støttet løsning: Keychain. I motsetning til eller eiendomslistefiler, krypterer Keychain data i hvile og håndhever strenge tilgangskontroller. Denne artikkelen gir en omfattende guide til å implementere sikker datalagring med Keychain, som dekker både den opprinnelige sikkerhetsrammen og praktiske beste praksis for produksjonsapplikasjoner.

Forstå iOS Keychain

Keychain er en sikker lagringsbeholder som administreres av operativsystemet. Den lagrer små, sensitive elementer - som passord, kryptografiske nøkler eller sertifikater - i en kryptert database. Data som er skrevet til Keychain er beskyttet selv når enheten er låst. Nøkkelfunksjoner inkluderer:

  • Kryptering i hvile ved bruk av maskinvarestøttet AES-256.
  • Access control via enhetspasskode, berørings-ID eller ansikts-ID.
  • Persistens på tvers av appen ominstallerer (om konfigurert) og valgfri iCloud synkronisering.
  • Isolasjon mellom apper: Som standard kan ikke én app lese en annen apps Keychain-elementer med mindre de deler en Keychain-tilgangsgruppe.

Keychain er ikke designet for store blobs; hold hvert element under noen få kilobyte. For større data, vurdere å bruke API eller rammeverket sammen med filbasert kryptering.

Keychain Services API vs. Tredjeparts biblioteker

Apple tilbyr det opprinnelige Keychain Services API (C-basert, ), som er kraftig men ekstra. Du kan bruke det direkte, eller vedta en Swift-vennlig wrapper. Populære tredjeparts biblioteker som KeychainAccess eller SwiftKeychainBruckper redusere kjeleplate. Men å forstå det underliggende API er viktig for feilsøking og når du trenger finkornet kontroll over tilgangspolicyer. I dag vil vi fokusere på det opprinnelige API med Swift.

Sette opp Keychain-lagring

Før du lagrer noe, må du bestemme på Keychain elementklasse. Den vanligste for generiske passord er . For Internett passord eller sertifikater er det andre klasser. Hvert element er referert av et sett attributter ⁇ en ordbok (CFDictionary) som beskriver elementet.

Grunnstrømmen følger alltid dette mønsteret:

  1. Bygg en spørringsordbok med elementklasse og attributter.
  2. funksjon (], , ], .
  3. Sjekk den returnerte (] eller en feilkode).

Før du skriver kode, importer sikkerhetsmodulen:

import Security
import Foundation // for Data and String utilities

Lagre data i keychain

Skrive et generisk passord

For å lagre et token (f.eks. en JWT) for den aktuelle brukeren:

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
}

Nøkkelpunkter:

  • fungerer som en primærnøkkel; velg en unik streng (f.eks. bruker-ID eller en konstant som ).
  • ] kontrollerer når elementet kan leses. Bruk for å sikre deg den beste sikkerheten; det hindrer iCloud-sikkerhetskopiering og begrenser tilgangen til den gjeldende enheten.
  • Vi ringer før vi legger til for å unngå å samle duplikat. Alternativt kan du bruke .

Legg til tilgangskontroll (Biometri eller Passcode)

For svært sensitive data, krever Touch ID eller Face ID før du leser:

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)

Nå vil enhver kalle til dette elementet utløse en biometrisk eller passkode-prompt. Bruk fra LocalAuthentication for å håndtere brukerinteraksjonen på en ypperlig måte.

Hente data fra Keychain

For å lese det lagrede polikset:

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
}

Sett til ] for å få tilbake dataene. Bruk til å hente et enkelt resultat. Hvis du utelater grensen, kan API returnere en tabell.

Viktig:] Når du bruker tilgangskontroll (biometri), kan -samtalen returnere hvis brukeren avbryter. Håndter dette tilfellet separat og aldri faller tilbake til vanlig tekstlagring.

Oppdaterer og sletter Keychain-elementer

Oppdaterer eksisterende element

I stedet for å slette og legge til på nytt, bruk :

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
}

Dette er mer effektivt enn et slette + tilføyelse, og det unngår potensielle løpsforhold.

Sletter et 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
}

Vær forsiktig med å ikke slette elementer som tilhører andre apper som deler samme tilgangsgruppe ⁇ alltid omfange spørringen din med hvis du bruker delte Keychains.

Adgangskontroll og tilgjengelighet

konstant definerer når nøkkelkjedeelementet kan leses. Velg det mest restriktive alternativet som fortsatt oppfyller appens behov:

AttributeMeaning
kSecAttrAccessibleWhenUnlockedAvailable only while device is unlocked (default).
kSecAttrAccessibleAfterFirstUnlockAvailable after device boots and is unlocked once. Allows background access.
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnlyRequires a passcode to be set. Strictest option—prevents access even after unlock if passcode is removed.
kSecAttrAccessibleWhenUnlockedThisDeviceOnlySame as WhenUnlocked but does not back up to iCloud, and cannot be restored to another device.

For de fleste apper, slår riktig balanse mellom sikkerhet og brukbarhet. Hvis du trenger å lese elementer i bakgrunnen (f.eks. en bakgrunnsoppdatering token), må du bruke (og aksepter at dataene er litt mindre beskyttet).

Feilhåndtering og vanlige pitfall

Funksjonene returnerer . Kontroller det alltid og håndterer feil på riktig måte.

  • ( ⁇ 25300) ⁇ Ingen ting samsvarer med spørringen.
  • ( ⁇ 25299) ⁇ Et element med samme primærnøkkel eksisterer allerede (hvis du ikke slettet først).
  • ( ⁇ 128) ⁇ Brukeren kansellert biometriske spørsmål.
  • ( ⁇ 25293) ⁇ Autentisering mislyktes eller biometrisk ikke tilgjengelig.

Aldri ignorere en ikke-vellykket status. Gracely nedgradere: vis en feilmelding eller prøv på nytt, men aldri lagre sensitive data utenfor Keychain som en reserve. Du kan bruke til å sjekke biometrisk tilgjengelighet før du prøver tilgang.

Beste praksis og produksjonsoverveielser

  • Bruk unike, beskrivende kontonavn per bruker eller per elementtype for å unngå kollisjoner.
  • ] Angi alltid en tilgjengelighetsattributt; ellers gjelder systemstandarden () som kanskje ikke er ideell.
  • Klart nøkkelkjededata når brukeren logger ut ⁇ titer over alle kjente kontoer og slette elementer.
  • Bruk Keychain Access Groups kun når du deler mellom dine egne apper. Unngå brede grupper.
  • lagrer aldri ikke-følsomme data (som brukerpreferanser) i Keychain-bruk eller en database i stedet.
  • Consider ved hjelp av med ]] for avanserte scenarier (macos Catalyst).
  • Test på en ekte enhet; Simulatoren bruker en programvare Keychain som oppfører seg annerledes enn maskinvarestøttet lagring.

Bruke Keychain med SwiftUI og Async/Await

For moderne apper, wrap Keychain operasjoner i en skuespiller eller en async-sikker klasse for å unngå blokkering av hovedtråden. Eksempel ved hjelp av :

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)
 }
}

Hvis du bruker biometriske elementer, kan -samtalen blokkere tråden mens du venter på brukerinteraksjon. Wrap den i en bakgrunnskø eller bedre, bruke s metode før Keychain-samtalen.

Konklusjon

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.