Εισαγωγή στην ασφαλή αποθήκευση δεδομένων στο iOS

Είτε αποθηκεύετε μάρκες ταυτότητας, κλειδιά κρυπτογράφησης ή ιδιωτικά διαπιστευτήρια, η πλατφόρμα παρέχει μια ειδική λύση με υποστήριξη υλικού: την Κλειδί]. Σε αντίθεση με [[LFT:0]]] ή αρχεία λίστας ακινήτων, το Κλειδί κρυπτογραφεί τα δεδομένα σε ηρεμία και επιβάλλει αυστηρούς ελέγχους πρόσβασης. Αυτό το άρθρο παρέχει έναν ολοκληρωμένο οδηγό για την εφαρμογή ασφαλούς αποθήκευσης δεδομένων με το Κλειδί, καλύπτοντας τόσο το πλαίσιο ασφάλειας των ιθαγενών όσο και πρακτικές βέλτιστες πρακτικές για εφαρμογές παραγωγής.

Κατανόηση του κλειδαρόκλειδου iOS

Το Keychain είναι ένα ασφαλές δοχείο αποθήκευσης που διαχειρίζεται το λειτουργικό σύστημα. Αποθηκεύει μικρά, ευαίσθητα αντικείμενα ⁇ όπως κωδικούς πρόσβασης, κρυπτογραφικά κλειδιά ή πιστοποιητικά ⁇ σε μια κρυπτογραφημένη βάση δεδομένων. Τα δεδομένα που είναι γραμμένα στο Keychain προστατεύονται ακόμα και όταν η συσκευή είναι κλειδωμένη.

  • Κρυπτογράφηση σε ηρεμία με χρήση hardware-backed AES-256.
  • Έλεγχος πρόσβασης μέσω κωδικού πρόσβασης συσκευής, Touch ID, ή Face ID.
  • Επιμονή σε επανεγκαταστάσεις εφαρμογών (αν έχει ρυθμιστεί) και προαιρετικό συγχρονισμό iCloud.
  • Απόλυση μεταξύ εφαρμογών: εξ ορισμού, μια εφαρμογή δεν μπορεί να διαβάσει τα αντικείμενα Keychain μιας άλλης εφαρμογής εκτός αν μοιράζονται μια ομάδα πρόσβασης Keychain.

Το μπρελόκ δεν είναι σχεδιασμένο για μεγάλες φούσκες, αλλά για μεγαλύτερα δεδομένα, εξετάστε το πλαίσιο [[LFT:1] ή το πλαίσιο [[LFT:2]] μαζί με κρυπτογράφηση βασισμένη στο αρχείο.

Υπηρεσίες κλειδαρόκερων API εναντίον Βιβλιοθηκών τρίτου μέρους

Η Apple παρέχει τις εγχώριες Υπηρεσίες μπρελόκ[[LFT:1]] API (C-based, [[LFT:3]]]), που είναι ισχυρό αλλά verbose. Μπορείτε να το χρησιμοποιήσετε άμεσα, ή να υιοθετήσετε ένα Swift-friendly wrapper. Δημοφιλείς βιβλιοθήκες τρίτων όπως [[LFT:2]]KeychainAccess[[[LFT:3]]] ή [[LFT:4]SwiftKeychainWrapper[]] μειώνουν το λεβητόριο. Ωστόσο, η κατανόηση του υποκείμενου API είναι απαραίτητη για την αποσφαλμάτωση και όταν χρειάζεστε λεπτοφυή έλεγχο πάνω από τις πολιτικές πρόσβασης.

⁇ αποθήκευσης κλειδαριών

Πριν από την αποθήκευση οτιδήποτε, πρέπει να αποφασίσετε για την Κλειδί αλυσίδας κατηγορίας στοιχείων. Το πιο κοινό για γενικούς κωδικούς πρόσβασης είναι . Για κωδικούς πρόσβασης στο διαδίκτυο ή πιστοποιητικά, υπάρχουν και άλλες κατηγορίες. Κάθε αντικείμενο αναφέρεται από ένα σύνολο χαρακτηριστικών ⁇ ένα λεξικό (CFDictionary) που περιγράφει το στοιχείο.

Η βασική ροή ακολουθεί πάντα αυτό το μοτίβο:

  1. Κατασκευή ενός λεξικού ερωτημάτων με την κλάση και τα χαρακτηριστικά του αντικειμένου.
  2. λειτουργία [, , , ].
  3. Ελέγξτε τον επιστρεφόμενο (] ή έναν κωδικό σφάλματος).

Πριν από την εγγραφή κώδικα, εισαγωγή της ενότητας ασφαλείας:

import Security
import Foundation // for Data and String utilities

Αποθήκευση δεδομένων στο μπρελόκ

Εγγραφή ενός γενικού κωδικού πρόσβασης

Για να αποθηκεύσετε ένα κουπόνι (π.χ., ένα JWT) για τον τρέχοντα χρήστη:

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
}

Βασικά σημεία:

  • ενεργεί ως πρωταρχικό κλειδί· επιλέγει μια μοναδική συμβολοσειρά (π.χ., το αναγνωριστικό χρήστη ή μια σταθερά όπως ).
  • ελέγχει όταν το στοιχείο μπορεί να διαβαστεί. Χρησιμοποιήστε [[LFT:17]] για καλύτερη ασφάλεια. Αποτρέπει το αντίγραφο ασφαλείας iCloud και περιορίζει την πρόσβαση στην τρέχουσα συσκευή.
  • Καλούμε πριν προσθέσουμε για να αποφύγουμε τη συσσώρευση διπλών αντικειμένων. Εναλλακτικά, μπορείτε να χρησιμοποιήσετε .

Προσθήκη ελέγχου πρόσβασης (Βιομετρία ή κωδικός πρόσβασης)

Για εξαιρετικά ευαίσθητα δεδομένα, απαιτείται το Touch ID ή το Face ID πριν από την ανάγνωση:

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)

Τώρα οποιαδήποτε κλήση για αυτό το στοιχείο θα ενεργοποιήσει μια βιομετρική ή κωδικό πρόσβασης. Χρησιμοποιήστε [[LFT:22]] από την ΤοπικήΑυθεντικότητα για να χειριστείτε την αλληλεπίδραση χρήστη με χάρη.

Ανάκτηση δεδομένων από το μπρελόκ

Για να διαβάσετε το αποθηκευμένο ενδεικτικό:

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
}

Ορισμός σε για να πάρετε πίσω τα δεδομένα. Χρησιμοποιήστε για να ανακτήσετε ένα μόνο αποτέλεσμα. Αν παραλείψετε το όριο, το API μπορεί να επιστρέψει μια συστοιχία.

Σημαντικό: Όταν χρησιμοποιείται έλεγχος πρόσβασης (βιομετρία), η κλήση μπορεί να επιστρέψει [ αν ο χρήστης ακυρώσει. Χειριστείτε αυτή την περίπτωση ξεχωριστά και ποτέ μην πέσετε πίσω στην αποθήκευση απλού κειμένου.

Ενημέρωση και διαγραφή αντικειμένων κλειδαριών

Ενημέρωση Υφιστάμενου Αντικειμένου

Αντί να διαγραφεί και να επαναπροστεθεί, χρησιμοποιήστε :

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
}

Αυτό είναι πιο αποτελεσματικό από ένα delete+add, και αποφεύγει τις πιθανές συνθήκες αγώνα.

Διαγραφή ενός αντικειμένου

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
}

Να είστε προσεκτικοί για να μην διαγράψετε αντικείμενα που ανήκουν σε άλλες εφαρμογές που μοιράζονται την ίδια ομάδα πρόσβασης ⁇ πάντα να καλύψετε το ερώτημά σας με αν χρησιμοποιείτε κοινόχρηστα μπρελόκ.

Χαρακτηριστικά ελέγχου και προσβασιμότητας πρόσβασης

Η σταθερά ορίζει ] πότε μπορεί να διαβαστεί το αντικείμενο του Keychain. Επιλέξτε την πιο περιοριστική επιλογή που εξακολουθεί να ικανοποιεί τις ανάγκες της εφαρμογής σας:

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.

Για τις περισσότερες εφαρμογές, επιτυγχάνει τη σωστή ισορροπία μεταξύ ασφάλειας και χρηστικότητας. Αν χρειαστεί να διαβάσετε τα στοιχεία στο παρασκήνιο (π.χ., ένα σημείο ανανέωσης φόντου), πρέπει να χρησιμοποιήσετε [ (και να αποδεχτείτε ότι τα δεδομένα είναι ελαφρώς λιγότερο προστατευμένα).

Σφάλμα χειρισμού και κοινών παγίδων

Οι λειτουργίες επιστρέφουν ]. Πάντα ελέγχουν και χειρίζονται κατάλληλα τις αποτυχίες.

  • ( ⁇ 25300) ⁇ Κανένα αντικείμενο δεν ταιριάζει με το ερώτημα.
  • ( ⁇ 25299) ⁇ Ένα αντικείμενο με το ίδιο πρωτεύον κλειδί υπάρχει ήδη (αν δεν διαγράψατε πρώτα).
  • ( ⁇ 28) ⁇ Ο χρήστης ακύρωσε βιομετρική αναγκαστική.
  • [[LFT:46]] ( ⁇ 25293) ⁇ Η ταυτοποίηση απέτυχε ή δεν είναι διαθέσιμη βιομετρική.

Ποτέ μην αγνοείτε μια κατάσταση μη επιτυχίας. Με χάρη υποβαθμίστε: εμφανίστε ένα μήνυμα σφάλματος ή ξαναδοκιμάστε, αλλά ποτέ μην αποθηκεύετε ευαίσθητα δεδομένα εκτός του Keychain ως εφεδρικό. Μπορείτε να χρησιμοποιήσετε [[LFT:47]] για να ελέγξετε τη βιομετρική διαθεσιμότητα πριν επιχειρήσετε την πρόσβαση.

Βέλτιστες Πρακτικές και Παραγωγικές Συνεκτάσεις

  • Χρησιμοποιήστε μοναδικά, περιγραφικά ονόματα λογαριασμού ανά χρήστη ή ανά τύπο αντικειμένου για την αποφυγή συγκρούσεων.
  • Πάντα να ορίσετε ένα χαρακτηριστικό προσβασιμότητας· διαφορετικά, ισχύει η προεπιλογή του συστήματος (]), η οποία μπορεί να μην είναι ιδανική.
  • Καθαρίστε τα δεδομένα κλειδαριών όταν ο χρήστης αποσυνδέεται ⁇ ⁇ τεωρήστε σε όλους τους γνωστούς λογαριασμούς και διαγράψτε τα στοιχεία.
  • Χρησιμοποιήστε τις ομάδες πρόσβασης κλειδαριών μόνο όταν μοιράζεστε μεταξύ των δικών σας εφαρμογών. Αποφύγετε τις ευρείες ομάδες.
  • Ποτέ μην αποθηκεύετε μη ευαίσθητα δεδομένα (όπως οι προτιμήσεις των χρηστών) στη χρήση του Keychain ή στη βάση δεδομένων.
  • Σχετικά με τη χρήση με για προηγμένα σενάρια (macos Catalyst).
  • Δοκιμάστε σε μια πραγματική συσκευή· ο Προσομοιωτής χρησιμοποιεί ένα λογισμικό Keychain που συμπεριφέρεται διαφορετικά από την αποθήκευση που υποστηρίζεται από υλικό.

Χρήση μπρελόκ με SwiftUI και Async/Await

Για σύγχρονες εφαρμογές, τύλιξε τις λειτουργίες Keychain σε έναν ηθοποιό ή σε μια τάξη ασφαλείας Async για να αποφύγει το μπλοκάρισμα του κύριου νήματος. Παράδειγμα χρησιμοποιώντας [[LFT:52]]:

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

Εάν χρησιμοποιείτε βιομετρικά, η κλήση μπορεί να μπλοκάρει το νήμα ενώ αναμένει αλληλεπίδραση χρήστη. Τυλίξτε το σε μια ουρά φόντου, ή καλύτερα, χρησιμοποιήστε [ τη μέθοδο πριν από την κλήση του Keychain.

Συμπέρασμα

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.