أمان تطبيقات Swift: أهم 10 ثغرات أمنية عمليًا
يتطلب تطوير التطبيقات المحمولة لنظامي iOS وmacOS اهتمامًا خاصًا بالأمان. Swift هي لغة قوية وحديثة، ولكنها حتى هي لا تحمي المطور من الأخطاء النمطية التي قد تؤدي إلى تسرب البيانات أو الاختراق أو الخسائر المالية. في هذه المقالة، سنستعرض أهم 10 ثغرات أمنية في تطبيقات Swift مع أمثلة كود حقيقية وتوصيات عملية لمعالجتها.
1. حقن SQL عبر Core Data وSQLite
يستخدم العديد من المطورين استعلامات SQL الخام مع SQLite. إذا لم يتم تنقية بيانات المستخدم، يمكن للمهاجم تنفيذ استعلام عشوائي على قاعدة البيانات.
// ❌ كود ضعيفlet query = "SELECT * FROM users WHERE name = '\\(userInput)'"
// ✅ كود آمن (استخدام الاستعلامات المُعلمة)let query = "SELECT * FROM users WHERE name = ?"var statement: OpaquePointer?sqlite3_prepare_v2(database, query, -1, &statement, nil)sqlite3_bind_text(statement, 1, (userInput as NSString).utf8String, -1, nil)استخدم الاستعلامات المُعلمة أو Core Data مع المسندات (predicates) - فهذا يمنع الحقن.
2. تخزين غير آمن للبيانات في Keychain
Keychain هو مكان آمن، لكن العديد من المطورين يضبطون سمات الوصول بشكل غير صحيح. على سبيل المثال، إذا لم يتم تحديد kSecAttrAccessible، فقد تكون البيانات متاحة بعد النسخ الاحتياطي.
// ❌ كود ضعيف (متاح بعد النسخ الاحتياطي)let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: "token", kSecValueData as String: tokenData]
// ✅ كود آمن (فقط عندما يكون الجهاز مفتوحًا)let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: "token", kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kSecValueData as String: tokenData]3. عدم التحقق من SSL/TLS (هجوم الوسيط)
أثناء التطوير، غالبًا ما يتم تعطيل التحقق من الشهادات للاختبار، ولكن يُنسى إعادة الإعدادات في الإصدار النهائي. وهذا يسمح للمهاجم باعتراض حركة المرور.
// ❌ كود ضعيف (خطير للإنتاج)let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)// في المفوض:func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))}
// ✅ كود آمن - استخدم التحقق القياسيlet session = URLSession.shared// أو استخدم ATS (App Transport Security) في Info.plistقم بتفعيل App Transport Security (ATS) ولا تقم بتعطيله في الإصدار النهائي.
4. تسرب البيانات عبر السجلات وCrashlytics
غالبًا ما يسجل المطورون بيانات حساسة (رموز مميزة، كلمات مرور) لأغراض التصحيح. إذا وصل السجل إلى خدمة خارجية (مثل Firebase Crashlytics)، ستتسرب البيانات.
// ❌ كود ضعيفprint("User token: \\(userToken)")
// ✅ كود آمن#if DEBUG print("User token: [REDACTED]")#endif// أو استخدم os_log بمستوى خصوصيةos_log("User token: %{private}@", log: .default, type: .debug, userToken)لا تقم أبدًا بتسجيل البيانات الحساسة في الإنتاج. استخدم الإخفاء أو التجميع الشرطي.
5. تخزين غير آمن للأسرار في الكود
تخزين مفاتيح API والرموز المميزة وكلمات المرور مباشرة في الكود المصدري هو خطأ فادح. حتى إذا كان الكود مشوشًا، يمكن استخراج السلاسل بسهولة من الملف الثنائي.
// ❌ كود ضعيفlet apiKey = "sk-1234567890abcdef"
// ✅ كود آمن - استخدم المصادقة من الخادم// أو قم بتشفير المفاتيح باستخدام CryptoKitimport CryptoKitlet key = SymmetricKey(data: "server_