Swift Application Security: Top 10 Vulnerabilities in Practice
Developing mobile applications for iOS and macOS requires special attention to security. Swift is a powerful and modern language, but even it does not protect developers from common mistakes that can lead to data leaks, hacking, or financial losses. In this article, we will break down the top 10 vulnerabilities of Swift applications with real code examples and practical recommendations for fixing them.
1. SQL Injections via Core Data and SQLite
Many developers use raw SQL queries with SQLite. If user input is not sanitized, an attacker can execute arbitrary queries against the database.
// ❌ Vulnerable codelet query = "SELECT * FROM users WHERE name = '\\(userInput)'"
// ✅ Secure code (using parameterized queries)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)Use parameterized queries or Core Data with predicates — this eliminates injections.
2. Insecure Data Storage in Keychain
Keychain is a secure place, but many developers incorrectly configure access attributes. For example, if kSecAttrAccessible is not specified, data may be accessible after backup.
// ❌ Vulnerable code (accessible after backup)let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: "token", kSecValueData as String: tokenData]
// ✅ Secure code (only when device is unlocked)let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: "token", kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly, kSecValueData as String: tokenData]3. Missing SSL/TLS Verification (Man-in-the-Middle)
During development, certificate verification is often disabled for testing, but developers forget to restore settings for release. This allows an attacker to intercept traffic.
// ❌ Vulnerable code (dangerous for production)let session = URLSession(configuration: .default, delegate: self, delegateQueue: nil)// In the delegate:func urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) { completionHandler(.useCredential, URLCredential(trust: challenge.protectionSpace.serverTrust!))}
// ✅ Secure code — use standard verificationlet session = URLSession.shared// Or use ATS (App Transport Security) in Info.plistEnable App Transport Security (ATS) and do not disable it in the release build.
4. Data Leakage via Logs and Crashlytics
Developers often log sensitive data (tokens, passwords) for debugging. If the log ends up in a third-party service (e.g., Firebase Crashlytics), data leaks.
// ❌ Vulnerable codeprint("User token: \\(userToken)")
// ✅ Secure code#if DEBUG print("User token: [REDACTED]")#endif// Or use os_log with privacy levelos_log("User token: %{private}@", log: .default, type: .debug, userToken)Never log sensitive data in production. Use masking or conditional compilation.
5. Insecure Storage of Secrets in Code
Storing API keys, tokens, and passwords directly in source code is a critical mistake. Even if the code is obfuscated, strings can be easily extracted from the binary file.
// ❌ Vulnerable codelet apiKey = "sk-1234567890abcdef"
// ✅ Secure code — use server-side authentication// Or encrypt keys using CryptoKitimport CryptoKitlet key = SymmetricKey(data: "server_