Sobes.tech
Middle

Gdzie przechowywałbyś chronione dane użytkownika?

sobes.tech AI

Odpowiedź od AI

Aby przechowywać chronione dane użytkownika na iOS, użyłbym następujących miejsc, w zależności od typu danych i poziomu bezpieczeństwa:

  1. Keychain Services:

    • Idealne do przechowywania niewielkich ilości poufnych informacji, takich jak hasła, tokeny autoryzacyjne, klucze szyfrowania.
    • Systemowy demon securityd zarządza keychain i przechowuje dane w zaszyfrowanej formie na dysku.
    • Obsługuje różne poziomy dostępności (np. dostępne tylko przy odblokowanym urządzeniu).
    • Dane są automatycznie synchronizowane przez iCloud Keychain, jeśli jest włączone.
    import Foundation
    import Security
    
    func savePassword(service: String, account: String, password: String) -> OSStatus {
        let data = password.data(using: .utf8)!
        let query: [String: Any] = [
            kSecClass as String: kSecClassGenericPassword,
            kSecAttrService as String: service,
            kSecAttrAccount as String: account,
            kSecValueData as String: data
        ]
    
        SecItemDelete(query as CFDictionary) // Usuwamy istniejący wpis
        return SecItemAdd(query as CFDictionary, nil) // Dodajemy nowy wpis
    }
    
  2. Zaszyfrowane Core Data / Realm:

    • Do przechowywania danych strukturalnych, wymagających szyfrowania na poziomie bazy danych.
    • Core Data obsługuje zewnętrzne rozszerzenia do szyfrowania (np. RNCryptor).
    • Realm Database ma wbudowane szyfrowanie pliku bazy danych.
    • Klucz szyfrowania musi być przechowywany w Keychain.
    // Przykład inicjalizacji Realm z szyfrowaniem
    import RealmSwift
    
    func setupEncryptedRealm() throws {
        var config = Realm.Configuration()
        if let key = KeychainHelper.getRealmEncryptionKey() { // Pobieramy klucz z Keychain
            config.encryptionKey = key
        } else {
            // Generujemy nowy klucz, zapisujemy go w Keychain i używamy
            let newKey = NSMutableData(length: 64)!
            _ = SecRandomCopyBytes(kSecRandomDefault, newKey.length, newKey.mutableBytes)
            KeychainHelper.saveRealmEncryptionKey(key: newKey as Data)
            config.encryptionKey = newKey as Data
        }
    
        let realm = try Realm(configuration: config)
        // Używamy Realm
    }
    
  3. API ochrony plików:

    • Do szyfrowania plików przechowywanych w systemie plików aplikacji.
    • iOS automatycznie szyfruje pliki na poziomie systemu plików za pomocą kluczy zarządzanych przez Secure Enclave.
    • Można ustawić różne tryby ochrony (np. complete, completeUnlessOpen, completeUntilFirstUserAuthentication, none) dla kontroli dostępu w zależności od stanu blokady urządzenia.
    func saveProtectedDataToFile(data: Data, fileName: String) throws {
        let fileManager = FileManager.default
        let urls = fileManager.urls(for: .documentDirectory, in: .userDomainMask)
        guard let documentDirectory = urls.first else { throw NSError(domain: "MyApp", code: 1, userInfo: [NSLocalizedDescriptionKey: "Nie znaleziono katalogu dokumentów"]) }
    
        let fileURL = documentDirectory.appendingPathComponent(fileName)
    
        // Ustawiamy tryb ochrony
        let options: Data.WritingOptions = .atomicWrite
        let protectedOptions: Data.WritingOptions = options.union(.writingWithoutOverwriting) // Przykład, rzeczywiste opcje ochrony ustawiane są atrybutami pliku lub w info.plist
    
        try data.write(to: fileURL, options: options)
    
        // Ustawianie atrybutów ochrony systemu plików (przykład)
        var attributes: [FileAttributeKey : Any] = [:]
        attributes[.protectionKey] = FileProtectionType.complete
        try fileManager.setAttributes(attributes, ofItemAtPath: fileURL.path)
    }
    
  4. Secure Enclave:

    • Sprzętowy koprocesor, odseparowany od głównego procesora, przeznaczony do przechowywania i obsługi kluczy kryptograficznych oraz danych.
    • Używany do Touch ID / Face ID i innych wrażliwych operacji.
    • Klucze przechowywane w Secure Enclave nigdy nie opuszczają jej i nie są dostępne dla głównego procesora ani systemu operacyjnego.
    • Idealnie nadaje się do generowania, przechowywania i używania kluczy do podpisu lub szyfrowania, gdzie ważna jest ochrona przed atakami programowymi.
    import CryptoKit
    import LocalAuthentication
    
    func generateAndStoreKeyInSecureEnclave() throws -> P256.Signing.PrivateKey {
        let accessControl = SecAccessControlCreateWithFlags(
            kCFAllocatorDefault,
            kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly, // Dostępne przy ustawionym haśle
            [.userPresence], // Wymaga uwierzytelnienia użytkownika (Touch ID/Face ID)
            nil
        )!
    
        let attributes: [String: Any] = [
            kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
            kSecAttrKeySizeInBits as String: 256,
            kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
            kSecPrivateKeyAttrs as String: [
                kSecAttrIsPermanent as String: true, // Klucz będzie trwały
                kSecAttrApplicationTag as String: "com.yourapp.signingkey".data(using: .utf8)!, // Unikalny tag
                kSecAttrAccessControl as String: accessControl
            ]
        ]
    
        var error: Unmanaged<CFError>?
        guard let privateKey = SecKeyCreateRandomKey(attributes as CFDictionary, &error) else {
            throw error!.takeRetainedValue() as Error
        }
    
        // Można uzyskać klucze z Secure Enclave po tagu do użycia
        return try P256.Signing.PrivateKey(rawRepresentation: privateKeyData) // Przykład, wymaga konwersji z SecKey na Data
    }
    

Wybór miejsca przechowywania zależy od:

  • Rozmiaru danych (mała objętość vs dane strukturalne).
  • Typu danych (hasło vs plik vs klucz kryptograficzny).
  • Wymaganego poziomu ochrony.
  • Potrzeby synchronizacji między urządzeniami (Keychain).
  • Potrzeby uwierzytelniania użytkownika do dostępu (Secure Enclave, File Protection).