Giới thiệu về kho dữ liệu bảo mật trên iOS

Đang bảo vệ dữ liệu nhạy nhạy là trách nhiệm cơ bản của bất kỳ ứng dụng iOS. Dù bạn đang cất giữ các vật chứng thực, khóa mã hóa, hay chứng thực cá nhân, nền tảng cung cấp một giải pháp đã đóng góp phần cứng được hỗ trợ: [FLT: 0] khả năng quản lý (FLT: 0) . Không như [FLT: 0] hoặc danh sách tài sản, tập tin mật mã Keychain tại phần còn lại và truy cập chặt chẽ. Bài này cung cấp hướng dẫn toàn diện để thực hiện dữ liệu bảo mật lưu bằng Keycha, cả hai khung bảo mật lẫn thực hiện tốt nhất cho ứng dụng.

Hiểu được mã iOS Keychain

Khóa là một công cụ lưu trữ bảo mật được quản lý bởi hệ điều hành. Nó chứa những mục nhỏ, nhạy cảm như mật khẩu, phím mật khẩu, hoặc các chứng nhận trong cơ sở dữ liệu đã mã hóa. Dữ liệu được viết vào Keychain được bảo vệ ngay cả khi thiết bị bị bị bị bị bị bị khóa. Khả năng chìa khóa bao gồm:

  • Dịch phụ sử dụng phần cứng AES-256.
  • Điều khiển qua mã thiết bị, ID chạm, hoặc Face ID.
  • Tính kiên trì qua ứng dụng cài đặt (nếu cấu hình) và tùy chọn iCuloud đồng bộ.
  • Sự phân giải giữa các ứng dụng: theo mặc định, một ứng dụng không thể đọc một mục Keychain khác, trừ khi họ chia sẻ một nhóm truy cập Keychain.

Khóachain không được thiết kế cho các đốm lớn; giữ mỗi mục dưới một vàikg. Để có dữ liệu lớn hơn, hãy xem xét sử dụng [FLT: 1) [FLT: 1] khung ) cùng với mã hóa tập tin.

Dịch vụ lõi Keychain và thư viện 3 phần

Apple cung cấp bản địa dịch vụ kiểu Keychain [FLT:] Dịch vụ [FLT:] , có quyền lực nhưng động từ. Bạn có thể sử dụng trực tiếp nó, hoặc chọn một thư viện dễ dùng [FLT: 1]. Tuy nhiên, việc hiểu biết về khí hậu [FLT: 2] khi bạn cần sửa lỗi [FL:3] hoặc [FT:wiftchaychay]wifrain [Fer] để kiểm soát các chính sách bản địa.

Thiết lập kho lưu trữ khóa

Trước khi cất giữ bất cứ điều gì, bạn phải quyết định lớp mục [FLT: 0] [FLT: 0] [FLT: 1]. Điểm phổ biến nhất cho mật khẩu chung . Đối với mật khẩu hay chứng nhận Internet, có các hạng khác. Mỗi mục được tham khảo bởi một tập hợp các thuộc tính (từ điển) mô tả mục đó.

Dòng chảy cơ bản luôn theo mẫu này:

  1. Xây dựng từ điển truy vấn với hạng mục và thuộc tính.
  2. Gọi chức năng thích hợp ([FLT: 6), , , .
  3. Kiểm tra trả về ( hoặc một mã lỗi).

Trước khi ghi mã, hãy nhập môđun bảo mật:

import Security
import Foundation // for Data and String utilities

Nhốt dữ liệu trong móc khóa

Viết một mật khẩu chung

Để lưu một biểu tượng (v. d., một JWT) cho người dùng hiện thời:

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
}

Điểm chính:

  • đóng vai trò là chìa khóa chính; chọn một chuỗi độc nhất (v., ID người dùng hoặc hằng số như .
  • điều khiển khi có thể đọc được mục. Hãy dùng để bảo mật tốt nhất; nó ngăn cản iCuloud sao lưu và hạn chế truy cập vào thiết bị hiện thời.
  • Chúng tôi gọi trước khi thêm vào để tránh tích lũy các mục trùng. Hoặc bạn có thể sử dụng .

Đang thêm điều khiển truy cập (bộ nhớ hay mã)

Để có dữ liệu nhạy cảm, cần phải xác định ID của chạm hoặc ID khuôn mặt trước khi đọc:

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)

Bây giờ bất kỳ gọi cho mục này sẽ kích hoạt một dấu nhắc sinh trắc học hoặc mật mã. Hãy dùng từ địa phươngAuthentication để xử lý tương tác người dùng một cách duyên dáng.

Thu thập dữ liệu từ móc khóa

Để đọc những biểu tượng đã lưu:

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
}

Đặt để lấy lại dữ liệu. Hãy dùng để lấy kết quả duy nhất. Nếu bạn bỏ qua giới hạn, hệ thống này có thể trở lại một dãy.

Người dịch:) Khi dùng khả năng điều khiển truy cập (dimery), cuộc gọi có thể trở lại nếu người dùng hủy bỏ. Hãy xử lý trường hợp này riêng lẻ và không bao giờ rơi vào kho lưu trữ văn bản thường.

Cập nhật và xoá các mục tổ hợp phím

Cập nhật mục đích đã có

Thay vì xóa và tiếp tục lại, hãy dùng :

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
}

Nó hiệu quả hơn việc xoá+add, và tránh điều kiện đua tiềm năng.

Xóa mục

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
}

Hãy cẩn thận để xoá các mục thuộc các ứng dụng khác chia sẻ cùng một nhóm truy cập. Luôn luôn bao gồm các yêu cầu nếu bạn dùng keychains chia sẻ.

Thuộc tính Điều khiển và Truy cập

Có thể đọc mục định nghĩa không ngừng khi [FLT:] mục Keychain (điều khóa chain). Hãy chọn tùy chọn hạn chế nhất vẫn đáp ứng nhu cầu của ứng dụng của bạn:

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.

Đối với phần lớn ứng dụng, đánh dấu sự cân bằng đúng giữa sự an toàn và khả năng sử dụng. Nếu bạn cần đọc các mục ở nền (v. d., một vật ghi chú nền để cập nhật), bạn phải dùng [[FLT: 40] (và chấp nhận rằng dữ liệu ít được bảo vệ hơn).

Giải quyết lỗi và cạm bẫy chung

Hàm trở lại . Luôn luôn kiểm tra và xử lý lỗi chính xác.

  • (–25300) – Không có món đồ nào khớp với yêu cầu.
  • (–25299) – Một món đồ có cùng phím chính đã có (nếu bạn không xóa trước).
  • (–128) – người dùng đã hủy dấu nhắc sinh trắc học.
  • (–25293) – xác thực thất bại hoặc sinh trắc học không có sẵn.

Không bao giờ bỏ qua trạng thái không thành công. Giảm nhẹ: hiển thị thông điệp lỗi hoặc thử lại, nhưng không bao giờ cất giữ dữ liệu nhạy cảm bên ngoài Keychain như là hậu phương. Bạn có thể dùng để kiểm tra tính năng sinh trắc học trước khi thử truy cập.

Những thực hành tốt nhất và sự cân nhắc sản xuất

  • Dùng tên tài khoản độc đáo [FLT: 1] cho mỗi người dùng hoặc mỗi mục để tránh va chạm.
  • Luôn luôn xác định một thuộc tính có khả năng truy cập ; nếu không, hệ thống mặc định () có thể áp dụng, mà có lẽ không phải là lý tưởng.
  • dữ liệu BAR khi người dùng ghi ra [FLT:] - phân loại trên tất cả các tài khoản đã biết và xoá mục.
  • Dùng nhóm truy cập Keychain chỉ khi chia sẻ các ứng dụng riêng. Tránh các nhóm rộng.
  • Không bao giờ lưu dữ liệu không nhạy cảm (như người dùng thích) trong Keychain - sử dụng hoặc một cơ sở dữ liệu thay thế.
  • Dùng với cho những kịch bản tiên tiến (macOS Catalyst).
  • Đặt trên một thiết bị thật ; mô phỏng sử dụng một keychain mà ứng dụng khác với lưu trữ phần cứng được hỗ trợ.

Dùng Keychin với RopeUI và Async/Awain

Đối với ứng dụng hiện đại, hãy kết thúc hoạt động Keychain trong một diễn viên hoặc một lớp học đặc biệt an toàn để tránh chặn sợi chính.

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

Nếu dùng sinh trắc học, cuộc gọi có thể chặn sợi trong khi chờ đợi tương tác người dùng.

Kết luận

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.