Вступ до безпечного зберігання даних на iOS

Захист конфіденційних даних користувачів є фундаментальною відповідальністю будь-якого додатка iOS. Незалежно від того, чи ви зберігаєте автентифікацію, ключі шифрування, або приватні облікові дані, платформа надає виділене рішення для апаратного забезпечення: Keychain]. На відміну від ] або файлів зі списку нерухомості, Keychain зашифрує дані в стані спокою і застосовує суворі контроль доступу. Ця стаття надає всебічний посібник для реалізації безпечного зберігання даних з Keychain, покриття як рідної основи безпеки, так і практичні кращі практики для виробничих додатків.

Розуміння ключа iOS

Keychain - це захищена контейнер зберігання, керована операційною системою. Він зберігає невеликі, чутливі елементи -наприклад, паролі, криптографічні ключі, або сертифікати - в зашифрованій базі. Дані, написані на Keychain, захищені навіть коли пристрій закривається. Ключові можливості включають:

  • Розшифрування на відпочинок з використанням апаратно-завантажених AES-256.
  • Контроль доступу через пристрій, кодування, сенсорний ідентифікатор або ідентифікатор особи.
  • Persistence через перевстановлення додатків (якщо налаштовано) і необов'язково синхронізація iCloud.
  • Isolation між додатками: за замовчуванням, один додаток не може прочитати інші елементи програми, якщо вони діляться групою доступу Keychain.

Ключове крісло не розраховане на великі баби, зберігає кожен елемент під кількома кілограмами. Для збільшення даних розглянемо за допомогою API або разом з шифруванням файлів.

API для ключів та інших послуг

Apple забезпечує рідну Keychain Services] API (C-на основі, ), яка є потужним, але дієслово. Ви можете використовувати його безпосередньо, або прийняти Swift-дружелюбний обгортковий. Популярні сторонні бібліотеки, такі як KeychainAccess або SwiftKeychainWrapper зменшити котелборд. Однак розуміння основного API є важливим для розвантаження і коли вам потрібно тонкозернувати політики доступу.

Налаштування клавіатури зберігання

Перед зберіганням все, ви повинні вирішити на Keychain class]. Найпоширеніші для генних паролів . Для інтернет-парольів або сертифікатів існують інші класи. Кожен пункт довідникається набором атрибутів — словник (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
}

Ключові моменти:

  • виступає в якості основного ключа; вибрати унікальний рядок (наприклад, ідентифікатор користувача або постійний, як ).
  • контролює, коли елемент можна прочитати. Використовуйте для найкращої безпеки; він запобігає шифруванню iCloud і обмежує доступ до поточного пристрою.
  • Ми називаємо перед додаванням, щоб уникнути накопичення дублікатів елементів. Крім того, ви можете використовувати .

Додавання контролю доступу (Біометрія або Паскод)

Для високочутних даних, необхідно вказати ідентифікатор Touch або ідентифікатор обличчя до читання:

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)

Тепер будь-який виклик для цього елемента запустить біометричний або прохідний код. Використовуйте з LocalAuthentication для обробки взаємодії користувача граціозно.

Відновлення даних з клавіатури

Щоб читати збережений токени:

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 може повернути масив.

Important: При використанні контролю доступу (біометрія), виклик може повернутися , якщо користувач скасовує. Рука цей випадок окремо і ніколи не спадають назад до простого текстового сховища.

Оновлення та видалення ключових елементів

Оновлення Existing предмета

Замість видалення та переадресації, використання :

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
}

Це більш ефективний, ніж видалення + занурення, і він дозволяє уникнути потенційних умов раси.

Видалення товару

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.

Для більшості додатків вдарив правий баланс між безпекою і інтуїтивністю. Якщо вам потрібно прочитати елементи на фоні (наприклад, фоновий освіжий токен), ви повинні використовувати (і прийняти, що дані трохи захищені).

Помилка Handling і Загальні Pitfalls

функції повертає . Завжди перевірте його і робіть помилки належним чином. Загальні помилки:

  • (–25300) – Немає пункту відповідає запиту.
  • (–25299) – Продукт з тим же основним ключем вже існує (якщо ви не видалите спочатку).
  • (–128) – Користувач відмінив біометричний запит.
  • (–25293) – Ауттентикація не була або біометрика не доступна.

Не ігноруйте статус несухного доступу. Грацільно деградуйте: покажіть повідомлення про помилку або ретри, але ніколи не зберігати конфіденційні дані поза Keychain як випадання. Ви можете використовувати для перевірки біометричної доступності перед спробою доступу.

Кращі практики та експертиза

  • Використовувати унікальні, дескриптивні імена рахунків за користувача або за тип елемента, щоб уникнути зіткнення.
  • Always вказати атрибут доступу; інакше, типовий режим системи (]) застосовується, що може бути ідеальною.
  • Зареєструвати дані ключа при вході користувача — перейменувати всі відомі облікові записи та видаляти елементи.
  • Використовувати групи доступу Keychain] тільки при обміні між власними додатками. Уникайте широкої групи.
  • Never store not-sensitive data (like user вподобання) in Keychain—use або бази даних замість.
  • Consider using ] для розширених сценаріїв (macOS Catalyst).
  • Test on the real device; Симулятор використовує програмне забезпечення Keychain, яке поводиться по-різному з апаратно-завантаженого сховища.

Використання Keychain з SwiftUI і Async / Await

Для сучасних додатків, загортання ключових операцій в акторі або асинхронному класі, щоб уникнути блокування основної нитки. Приклад використання :

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.