Keychain (حلقه کلیدها) — مخزن محفوظی در iOS است که برای ذخیره ایمن رمزها، کلیدهای رمزنگاری، گواهینامهها و یادداشتهای محرمانه طراحی شده است. به گزارش Apple Security Documentation (2025)، Keychain از رمزگذاری سختافزاری از طریق Secure Enclave در تمامی دستگاههای با تراشه A7 و جدیدتر استفاده میکند. درک معماری iOS Keychain برای هر توسعهدهنده برای ذخیره صحیح توکنها و اسرار برنامه ضروری است.
نکات کلیدی
iOS Keychain — یک مکانیزم ذخیره محفوظ برای دادههای محرمانه است که در سیستمعامل Apple ساخته شده است. بر خلاف UserDefaults یا فایلهای عادی، Keychain تمامی عناصر را در سطح سختافزار رمزگذاری میکند و کنترل دسترسی ریزی بر اساس سیاستهای امنیتی ارائه میدهد.
Keychain در iOS 2.0 معرفی شد و از آن زمان تغییرات قابل توجهی را تجربه کرده است: در iOS 7 پشتیبانی از کلیدهای سختافزاری از طریق Secure Enclave، در iOS 9 تقسیم Keychain بین برنامهها از طریق Access Groups، در iOS 13 پشتیبانی از پیوند بیومتریک از طریق LAContext اضافه شد. به گزارش Apple WWDC Session (2024)، بیش از 90% برنامههای iOS در صد برتر App Store از Keychain برای ذخیره توکنهای احراز هویت استفاده میکنند.
از نظر معماری، Keychain یک پایگاه داده SQLite رمزگذاریشده است که در خارج از سندبوق برنامه قرار دارد. هر عنصر (SecItem) با یک کلید جداگانه رمزگذاری میشود که آن کلید نیز توسط کلید سختافزاری Secure Enclave محافظت میشود. خدمت سیستمی Securityd دسترسی به Keychain را بر اساس مجوزهای برنامه (entitlements) و کلاس حفاظت مورد نیاز مدیریت میکند.
مزیت مهم Keychain نسبت به سایر روشهای ذخیره: دادهها به طور خودکار توسط سیستمعامل رمزگذاری و رمزگشایی میشوند. توسعهدهنده نیازی به پیادهسازی دستی رمزنگاری ندارد — کافی است SecItemAdd را با پارامترهای صحیح فراخوانی کند. iOS تضمین میدهد که دادههای Keychain توسط سایر برنامهها قابل خواندن نباشند (در صورت پیکربندی صحیح Access Groups).
معماری Keychain چند سطح را شامل میشود: فیزیکی (Secure Enclave)، سیستمی (Security.framework)، کاربردی (API SecItem*) و منطقی (Access Groups، Protection Classes). درک هر سطح به طراحی صحیح ذخیره اسرار کمک میکند.
API اصلی برای کار با Keychain توابع Security framework هستند: SecItemAdd برای افزودن، SecItemCopyMatching برای خواندن، SecItemUpdate برای بهروزرسانی و SecItemDelete برای حذف. هر تابع یک فرهنگ query دریافت میکند که ویژگیهای عنصر مورد جستجو یا ذخیره شده را توصیف میکند.
ویژگیهای کلیدی query: kSecClass — نوع عنصر (kSecClassGenericPassword، kSecClassKey، kSecClassCertificate)، kSecAttrAccount — شناسه یکتا در داخل کلاس، kSecValueData — دادههای ذخیرهشده (Data)، kSecAttrAccessible — کلاس حفاظت. SecItemCopyMatching با علم kSecReturnData دادههای عنصر را ، با kSecMatchLimit تعداد نتایج را برمیگرداند.
مهم: تمام توابع وضعیت OSStatus را برمیگردانند. عملیات موفق errSecSuccess (0) را برمیگرداند. خطاها: errSecItemNotFound (25300-) — عنصر پیدا نشد، errSecDuplicateItem (25299-) — عنصر قبلاً وجود دارد، errSecAuthFailed (25293-) — احراز هویت بیومتریک انجام نشد. توسعهدهنده باید هر وضعیت را به طور صحیح مدیریت کند.
Protection Class — ویژگی kSecAttrAccessible است که مشخص میکند دادهها در Keychain کی برای خواندن دسترسی هستند. iOS شش کلاس حفاظت با سطوح مختلف دسترسی و امنیت پشتیبانی میکند.
کلاس توصیهشده برای اکثر سناریوها kSecAttrAccessibleWhenUnlockedThisDeviceOnly است: دادهها فقط در حالت باز بودن دستگاه دسترسی هستند و به iCloud Backup کپی نمیشوند. برای دادههایی که باید پس از راهاندازی دسترسی باشند (اما فقط پس از اولین باز کردن)، از kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly استفاده کنید. برای دادههای حساس که در هر دسترسی احراز هویت بیومتریک نیاز دارند، kSecAttrAccessibleWhenUnlockedThisDeviceOnly را با ACL حاوی بیومتری ترکیب کنید.
کلاسهای بدون پسوند ThisDeviceOnly (kSecAttrAccessibleWhenUnlocked، kSecAttrAccessibleAfterFirstUnlock) اجازه کپی در iCloud Backup را میدهند. این برای کاربر راحت است اما امنیت را کاهش میدهد — دادهها میتوانند از پشتیبان گیری بازیابی شوند. برای توکنهای احراز هویت همیشه از ThisDeviceOnly استفاده کنید.
Access Control List (ACL) — مکانیزمی است که عملیات روی عنصر Keychain را بر اساس احراز هویت کاربر محدود میکند. ACL از طریق SecAccessControlCreateWithFlags ایجاد و در زمان ذخیره عنصر به ویژگی kSecAttrAccessControl ارسال میشود.
علمهای پشتیبانیشده: kSecAccessControlUserPresence — هر نوع احراز هویت (Face ID، Touch ID یا کد عبور)، kSecAccessControlBiometryCurrentSet — فقط بیومتری (اثر انگشت یا چهره ثبتشده در حال حاضر)، kSecAccessControlDevicePasscode — فقط کد عبور. ACL به هر عملیات اعمال میشود: خواندن، بهروزرسانی و حذف عنصر نیز به احراز هویت نیاز دارند.
در iOS 15+ علم kSecAccessControlWatch اضافه شد — برای Apple Watch، که اجازه احراز هویت از طریق ساعت همراه را میدهد. ACL قابل ترکیب است: به عنوان مثال، kSecAccessControlUserPresence یا kSecAccessControlBiometryAny با کد عبور اختیاری (سوگیری .or).
iOS Keychain از چهار کلاس اصلی عناصر (kSecClass) پشتیبانی میکند، هر کدام برای نوع داده خود طراحی شده است. انتخاب صحیح کلاس سازماندهی و جستجوی عناصر را سادهتر میکند.
kSecClassGenericPassword — رمز عبور عمومی: پرکاربردترین کلاس. دادههای دلبخواه دودویی (Data) را با یک کلید متفرد (kSecAttrAccount) ذخیره میکند. برای توکنها، کلیدهای API، کدهای PIN مناسب است. برای استفاده به مجوزهای اضافی (entitlements) نیاز ندارد.
kSecClassInternetPassword — رمز عبور اینترنت: دادههای مرتبط با منابع شبکه را ذخیره میکند. ویژگیهای اضافی: kSecAttrServer (دامنه سرور)، kSecAttrProtocol (https، ftp)، kSecAttrPort، kSecAttrAuthenticationType. iOS میتواند چنین رمزهایی را از طریق AutoFill به طور خودکار پر کند.
kSecClassKey — کلید رمزنگاری: برای ذخیره کلیدهای رمزنگاری (AES، RSA، EC). کلید به عنوان SecKeyRef ذخیره میشود، نه Data. kSecClassCertificate — گواهینامه X.509 برای ذخیره و تأیید گواهینامههای دیجیتال. هر دو کلاس نیازمند درک عملیات رمزنگاری و پیکربندی صحیح ویژگیها هستند.
در عمل، 95% موارد استفاده از Keychain در برنامههای موبایل توسط kSecClassGenericPassword برای ذخیره توکنهای احراز هویت و kSecClassKey برای ذخیره کلیدهای شخصی رمزنگاری پوشش داده میشوند. kSecClassCertificate به ندرت استفاده میشود — معمولاً در برنامههای سازمانی با PKI خود.
نمونههای عملی کار با Keychain در Swift با استفاده از Security framework را بررسی میکنیم. هر نمونه شامل مدیریت خطا و پیکربندی صحیح Protection Class است.
نمونه پایه توکن احراز هویت را در Keychain با حفاظت WhenUnlockedThisDeviceOnly ذخیره میکند. کلید (kSecAttrAccount) شناسه خدمت است، دادهها (kSecValueData) توکن در قالب Data هستند.
import Security
enum KeychainError: Error {
case unexpectedStatus(OSStatus)
}
func saveToken(token: String, service: String) throws {
let data = Data(token.utf8)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: "auth_token",
kSecValueData as String: data,
kSecAttrAccessible as String:
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.unexpectedStatus(status)
}
}
func readToken(service: String) throws -> String {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrService as String: service,
kSecAttrAccount as String: "auth_token",
kSecReturnData as String: true,
kSecMatchLimit as String: kSecMatchLimitOne
]
var result: AnyObject?
let status = SecItemCopyMatching(
query as CFDictionary, &result
)
guard status == errSecSuccess,
let data = result as? Data else {
throw KeychainError.unexpectedStatus(status)
}
return String(decoding: data, as: UTF8.self)
}
نمونه استفاده از SecAccessControlCreateWithFlags را برای پیوند کلید به بیومتری نشان میدهد. هر دسترسی به عنصر نیازمند Face ID یا Touch ID خواهد بود.
import LocalAuthentication
func saveWithBiometry(data: Data, key: String) throws {
let accessControl = SecAccessControlCreateWithFlags(
nil,
kSecAttrAccessibleWhenUnlockedThisDeviceOnly,
.biometryCurrentSet,
nil
)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: key,
kSecValueData as String: data,
kSecAttrAccessControl as String: accessControl as Any
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw KeychainError.unexpectedStatus(status)
}
}
نمونه راهاندازی Access Group را برای دسترسی مشترک به Keychain بین برنامههای یک توسعهدهنده نشان میدهد. نیازمند entitlement keychain-access-groups است.
// Capabilities: Keychain Sharing فعال است
// App IDs: group.com.example.shared
func saveSharedToken(token: Data) {
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "shared_token",
kSecValueData as String: token,
kSecAttrAccessGroup as String:
"group.com.example.shared",
kSecAttrAccessible as String:
kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemAdd(query as CFDictionary, nil)
}
استفاده صحیح از iOS Keychain نیازمند رعایت چند قاعده کلیدی است که از آسیبپذیریهای رایج و از دست رفتن دادهها جلوگیری میکنند.
برای همه اسرار احراز هویت از ThisDeviceOnly استفاده کنید: kSecAttrAccessibleWhenUnlockedThisDeviceOnly تضمین میدهد توکنها به iCloud Backup وارد نشوند. اگر یک مهاجم به پشتیبان دسترسی پیدا کند، دادههای Keychain با این علم دسترسی نخواهند بود. استثنا دادههایی هستند که باید در تمام دستگاههای کاربر دسترسی باشند (مانند کلیدهای رمزنگاری برای خدمات خود)، که برای آنها از kSecAttrAccessibleWhenUnlocked با kSecAttrSynchronizable استفاده کنید.
رمزهای خام را ذخیره نکنید — هشها یا توکنهای جلسه را ذخیره کنید. Apple Security Guide (2025) توصیه میکند هرگز رمز عبور کاربر را به صورت متن ساده در Keychain ذخیره نکنید. به جای آن، refresh token دریافتی از سرور پس از احراز هویت موفق از طریق OAuth 2.0 را ذخیره کنید. رمز عبور فقط برای دریافت توکن استفاده شده و بلافاصله از حافظه پاک میشود.
خطاهای Keychain را به طور صحیح مدیریت کنید: هر عملیات با Keychain OSStatus را برمیگرداند که باید بررسی شود. توجه ویژه به errSecItemNotFound (توکن منقضی یا حذف شده) و errSecAuthFailed (بیومتری انجام نشد). در حالت اول، برنامه باید احراز هویت جدید بخواهد، در حالت دوم — روش جایگزین (کد عبور) را نشان دهد. هرگز وضعیت errSecItemNotFound را نادیده نگیرید — این باعث سقوط برنامه در حال خواندن nil میشود.
Keychain را روی دستگاه واقعی تست کنید: شبیهساز Secure Enclave ندارد و از ACLهای بیومتریک پشتیبانی نمیکند. همیشه سناریوهای زیر را بررسی کنید: اولین راهاندازی، بازیابی از پشتیبان، تغییر رمز دستگاه، حذف و نصب مجدد برنامه. روی دستگاه واقعی، Keychain پس از حذف برنامه حفظ میشود، اما فقط اگر از علم kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly استفاده نشده باشد — این علم با حذف کد عبور پاک میشود.
تعداد عملیات Keychain را بهینه کنید: هر عملیات خواندن یا نوشتن یک فراخوانی از خدمت سیستمی Securityd است که میتواند ترد را مسدود کند. توکنهای خوانده شده را برای مدت جلسه در حافظه ذخیره کنید و فقط در زمان راهاندازی مجدد برنامه یا خطای احراز هویت (401 از سرور) به Keychain مراجعه کنید. iOS به طور خودکار Keychain را در زمان قفل شدن دستگاه قفل میکند، بنابراین خواندن را از طریق LAContext با درخواست بیومتری برنامهریزی کنید.
سوالات متداول
بله، از کلاس حفاظت kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly یا kSecAttrAccessibleAfterFirstUnlock استفاده کنید. دادهها پس از اولین باز کردن دستگاه پس از راهاندازی دسترسی خواهند بود. برای دسترسی خودکار در زمان راهاندازی برنامه (بدون انتظار باز کردن) از kSecAttrAccessibleAlways استفاده کنید، اما این امنیت را کاهش میدهد.
SecItemDelete را با query حاوی kSecClass برای هر نوع داده فراخوانی کنید. برای پاکسازی کامل تمام عناصر برنامه: SecItemDelete([kSecClass as String: kSecClassGenericPassword] as CFDictionary). برای kSecClassKey، kSecClassCertificate و kSecClassInternetPassword تکرار کنید.
kSecAttrAccessible مشخص میکند دادهها کی دسترسی هستند (در باز کردن، پس از اولین باز کردن و الخ). kSecAttrAccessControl مشخص میکند چه کسی میتواند دسترسی داشته باشد (بیومتری، کد عبور، هر نوع احراز هویت). آنها ترکیب میشوند: ابتدا Protection Class، سپس ACL. مثالاً، دادهها فقط در باز کردن و فقط پس از Face ID دسترسی هستند.
دلایل: عنصر هرگز ذخیره نشده، عنصر پس از حذف کد عبور پاک شده (اگر kSecAttrAccessibleWhenPasscodeSet استفاده شده باشد)، برنامه مجدداً نصب شده (Keychain حفظ میشود اما از پشتیبان روی دستگاه جدید بازیابی نمیشود)، Access Group یا شناسه تیم توسعهدهنده تغییر کرده. kSecAttrService و kSecAttrAccount را بررسی کنید.
از LAContext از LocalAuthentication استفاده کنید: context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) را فراخوانی کنید. اگر true برگرداند — دستگاه از Touch ID یا Face ID پشتیبانی میکند. برای ACL Keychain از علم biometryCurrentSet (فقط دادههای بیومتریک فعلی) یا biometryAny (هر داده بیومتریک ثبتشده قبلی) استفاده کنید.
نتیجهگیری
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید