Przechowywanie sekretów w Keychain
Bezpieczne zapisywanie i pobieranie danych uwierzytelniających.
Przechowywanie sekretów w Keychain to bezpłatna lekcja Swift Academy na CoddyKit. To lekcja 1 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Swift Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Swift Academy zawiera 4 lekcji w sumie.
Dlaczego używa się Keychain
Haseł, tokenów i kluczy nigdy nie należy przechowywać w UserDefaults ani w zwykłych plikach — można je łatwo odczytać. Keychain to szyfrowana baza danych zarządzana przez system operacyjny, przeznaczona do przechowywania niewielkich sekretów i chroniona przez sprzęt oraz kod urządzenia użytkownika. To jedyne właściwe miejsce na dane uwierzytelniające.
import Security
// Keychain stores secrets encrypted at rest,
// survives app updates, and gates access by policy.Elementy są słownikami
Keychain Services API jest oparte na języku C: element opisuje się za pomocą słownika zapytania [String: Any], korzystając z kluczy będących stałymi kSec.... Ten sam kształt słownika wykorzystuje się podczas dodawania, wyszukiwania, aktualizowania i usuwania.
import Security
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "user@example.com",
kSecAttrService as String: "com.example.app"
]
_ = queryKlasy elementów
Klucz kSecClass określa typ elementu. kSecClassGenericPassword obejmuje tokeny i sekrety aplikacji, natomiast kSecClassInternetPassword służy do przechowywania danych uwierzytelniających serwera wraz z atrybutami hosta i protokołu. Większość sekretów aplikacji należy przechowywać jako hasła ogólne.
import Security
// kSecClassGenericPassword -> tokens, API keys
// kSecClassInternetPassword -> server logins
// kSecClassKey / Certificate -> crypto material
let cls = kSecClassGenericPassword
_ = clsDodawanie elementu
SecItemAdd wstawia nowy element. Dane należy umieścić pod kluczem kSecValueData jako Data, a następnie dodać atrybuty, które go identyfikują. Metoda zwraca wartość OSStatus — errSecSuccess oznacza powodzenie.
import Security
func save(_ token: String, account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: Data(token.utf8)
]
return SecItemAdd(query as CFDictionary, nil)
== errSecSuccess
}Obsługa duplikatów
Dodanie elementu, którego atrybuty identyfikujące już istnieją, zwraca errSecDuplicateItem. Solidna procedura zapisu najpierw próbuje wywołać SecItemAdd, a w przypadku duplikatu przechodzi do SecItemUpdate — jest to schemat upsert.
import Security
func upsert(_ data: Data, account: String) -> Bool {
let base: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
var add = base
add[kSecValueData as String] = data
let status = SecItemAdd(add as CFDictionary, nil)
if status == errSecDuplicateItem {
return SecItemUpdate(base as CFDictionary,
[kSecValueData as String: data] as CFDictionary)
== errSecSuccess
}
return status == errSecSuccess
}Odczytywanie elementu
SecItemCopyMatching wyszukuje elementy. Aby uzyskać bajty sekretu, należy ustawić kSecReturnData na true, a kSecMatchLimit na kSecMatchLimitOne. Wynik jest zwracany przez parametr wyjściowy jako CFTypeRef.
import Security
func load(account: String) -> Data? {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne]
var result: CFTypeRef?
let status = SecItemCopyMatching(
query as CFDictionary, &result)
guard status == errSecSuccess else { return nil }
return result as? Data
}Aktualizowanie elementu
SecItemUpdate przyjmuje dwa słowniki: zapytanie wyszukujące element oraz słownik atrybutów do aktualizacji. Zmieniają się tylko wymienione atrybuty; wszystkie pozostałe zostają zachowane.
import Security
func update(_ newData: Data, account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
let attrs: [String: Any] = [
kSecValueData as String: newData]
return SecItemUpdate(query as CFDictionary,
attrs as CFDictionary) == errSecSuccess
}Usuwanie elementu
SecItemDelete usuwa pasujące elementy. Usunięcie elementu, który nie istnieje, zwraca errSecItemNotFound. Podczas usuwania danych uwierzytelniających przy wylogowywaniu można potraktować ten wynik jako powodzenie.
import Security
func delete(account: String) -> Bool {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account]
let status = SecItemDelete(query as CFDictionary)
return status == errSecSuccess
|| status == errSecItemNotFound
}Dekodowanie OSStatus
Błędy są całkowitymi kodami OSStatus. SecCopyErrorMessageString zamienia taki kod na zrozumiały dla człowieka opis, co jest nieocenione podczas debugowania przyczyn nieudanego zapisu lub odczytu.
import Security
func describe(_ status: OSStatus) -> String {
return SecCopyErrorMessageString(status, nil)
as String? ?? "OSStatus \(status)"
}Jednoznaczne identyfikowanie elementów
Elementy są dopasowywane na podstawie kombinacji atrybutów — zazwyczaj kSecAttrService i kSecAttrAccount w przypadku haseł ogólnych. Należy wybrać stabilny, specyficzny dla aplikacji ciąg usługi, aby różne sekrety nigdy się nie zderzały.
import Security
// Uniqueness for generic passwords usually comes from:
// service (your bundle id) + account (the username)
let service = "com.example.app.auth"
let account = "current-user"
_ = (service, account)Niewielki wrapper
Ponieważ surowe API jest rozwlekłe, zespoły opakowują je w niewielki typ udostępniający metody save, read i delete. Dzięki temu formalności związane z kSec znajdują się w jednym miejscu, a miejsca wywołań pozostają przejrzyste.
import Security
struct TokenStore {
let service = "com.example.app.auth"
func read(_ account: String) -> Data? {
let q: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: account,
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne]
var out: CFTypeRef?
return SecItemCopyMatching(q as CFDictionary, &out)
== errSecSuccess ? out as? Data : nil
}
}Szybkie sprawdzenie
Proszę przypomnieć sobie właściwy sposób pobierania bajtów zapisanego sekretu.
Podsumowanie
Poznali Państwo operacje CRUD w Keychain:
- Dane uwierzytelniające należy przechowywać w zaszyfrowanym Keychain, nigdy w UserDefaults ani w plikach.
- Elementy należy opisywać za pomocą słowników zapytań
kSec; należy wybrać klasę, taką jakkSecClassGenericPassword. SecItemAdd/SecItemCopyMatching/SecItemUpdate/SecItemDeleteobsługują cały cykl życia;errSecDuplicateItemnależy obsłużyć za pomocą operacji upsert.- Elementy należy identyfikować na podstawie usługi i konta, a kody
OSStatusdekodować na potrzeby debugowania.
Ucz się Swift dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 122
- Lekcje
- 409
Często zadawane pytania
Czy lekcja „Przechowywanie sekretów w Keychain” jest bezpłatna?
Tak — pełny tekst „Przechowywanie sekretów w Keychain” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Swift Academy, przejdź na CoddyKit PRO. Kurs Swift Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Przechowywanie sekretów w Keychain”?
Bezpieczne zapisywanie i pobieranie danych uwierzytelniających. Ćwiczysz Swift Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Swift Academy?
Nie wymagamy żadnego doświadczenia. Swift Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 1 z 4.
Ile czasu zajmuje lekcja „Przechowywanie sekretów w Keychain”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Swift Academy?
Tak. Każda lekcja Swift Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- Przechowywanie sekretów w Keychain
- Kontrola dostępu do Keychain
- Uwierzytelnianie biometryczne
- Ochrona danych i szyfrowanie