Swift 应用安全:十大漏洞及防护方法
使用 Swift 开发 iOS 和 macOS 应用时需要特别关注安全性。即使是一个小错误也可能导致数据泄露、黑客入侵或设备被攻破。本文将分析 Swift 应用中最常见的 10 个漏洞,展示危险代码示例,并提供实用的修复建议。
1. 不安全的数-据存储
最常见的错误是将机密数据未加密地保存在 UserDefaults 或 Plist 文件中。
问题代码:
UserDefaults.standard.set("password123", forKey: "userPassword")UserDefaults.standard.synchronize()安全解决方案:使用 Keychain Services 或 Data Protection API。
import Security
func savePassword(_ password: String, for account: String) { let data = password.data(using: .utf8)! let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: account, kSecValueData as String: data, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ] SecItemAdd(query as CFDictionary, nil)}2. 输入验证
用户输入经常包含恶意数据。始终要对输入进行验证和清理。
SQL 注入示例:
let query = "SELECT * FROM users WHERE name = '\\(userInput)'" // 危险!防护措施:使用参数化查询。
let query = "SELECT * FROM users WHERE name = ?"let statement = try database.prepare(query)try statement.bind([userInput])3. 不安全的网络通信
使用 HTTP 而不是 HTTPS,忽略 SSL 证书。
危险代码:
let url = URL(string: "http://example.com/api")! // HTTP,而非 HTTPSlet task = URLSession.shared.dataTask(with: url) { data, response, error in // 数据处理}安全方法:
let url = URL(string: "https://example.com/api")!let config = URLSessionConfiguration.defaultconfig.tlsMinimumSupportedProtocolVersion = .TLSv12let session = URLSession(configuration: config)let task = session.dataTask(with: url) { data, response, error in guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else { return } // 数据处理}task.resume()4. WebView 注入
如果在未加限制的情况下使用 WKWebView,攻击者可以执行 JavaScript 代码。
易受攻击的代码:
webView.loadHTMLString(htmlContent, baseURL: nil)防护措施:禁用 JavaScript 或使用内容安全策略。
let config = WKWebViewConfiguration()let preferences = WKPreferences()preferences.javaScriptEnabled = falseconfig.preferences = preferenceslet webView = WKWebView(frame: .zero, configuration: config)5. 硬编码密钥
切勿将 API 密钥、令牌或密码存储在源代码中。
错误做法:
let apiKey = "sk_live_1234567890abcdef"正确做法:使用 .env 文件、服务器端授权或云服务(例如 AWS Secrets Manager)。
// Info.plist 中的加密密钥if let apiKey = Bundle.main.object(forInfoDictionaryKey: "API_KEY") as? String { // 使用该密钥}