Swift Academy · Lekcja

Przechowywanie sekretów w Keychain

Bezpieczne zapisywanie i pobieranie danych uwierzytelniających.

Lekcja 1 z 413 kroki

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"
]
_ = query

Klasy 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
_ = cls

Dodawanie 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ą jak kSecClassGenericPassword.
  • SecItemAdd / SecItemCopyMatching / SecItemUpdate / SecItemDelete obsługują cały cykl życia; errSecDuplicateItem należy obsłużyć za pomocą operacji upsert.
  • Elementy należy identyfikować na podstawie usługi i konta, a kody OSStatus dekodować na potrzeby debugowania.
Bezpłatny start

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

  1. Przechowywanie sekretów w Keychain
  2. Kontrola dostępu do Keychain
  3. Uwierzytelnianie biometryczne
  4. Ochrona danych i szyfrowanie
← Powrót do Swift Academy