Безопасность Swift приложений: топ-10 уязвимостей для начинающих
Разработка на Swift для iOS и macOS требует особого внимания к безопасности. Даже небольшие ошибки могут привести к утечке данных или взлому приложения. В этой статье мы разберём 10 самых распространённых уязвимостей, с которыми сталкиваются начинающие разработчики, и покажем, как их избежать.
1. Небезопасное хранение данных в UserDefaults
Многие новички сохраняют конфиденциальные данные (пароли, токены) в UserDefaults. Это зона общего пользования, данные хранятся в открытом виде.
// ❌ Опасно
UserDefaults.standard.set("secret_password", forKey: "userPass")
// ✅ Безопасно — используем Keychain
import Security
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "userPass",
kSecValueData as String: "secret_password".data(using: .utf8)!
]
SecItemAdd(query as CFDictionary, nil)
2. Инъекции SQL в Core Data
Использование строковых предикатов с пользовательским вводом открывает путь к SQL-инъекциям.
// ❌ Уязвимо
let input = "admin' OR '1'='1"
let predicate = NSPredicate(format: "name == %@", input)
// ✅ Безопасно — используем параметризированные запросы
let safePredicate = NSPredicate(format: "name == %@", input)
3. Отсутствие валидации ввода
Доверие к пользовательскому вводу — одна из главных ошибок. Злоумышленник может отправить вредоносные данные через текстовые поля.
func processUserInput(_ text: String) {
// ❌ Пропускаем любой ввод
let url = URL(string: "https://api.example.com/search?q=\(text)")
// ✅ Проверяем и экранируем
guard let encoded = text.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
let safeUrl = URL(string: "https://api.example.com/search?q=\(encoded)")
}
4. Небезопасное сетевое соединение (HTTP вместо HTTPS)
Использование HTTP делает данные уязвимыми для перехвата. Всегда используйте HTTPS.
// ❌ Опасно
let config = URLSessionConfiguration.default
config.waitsForConnectivity = false
// ✅ Безопасно — принудительно HTTPS
let config = URLSessionConfiguration.default
config.connectionProxyDictionary?.removeAll()
config.tlsMinimumSupportedProtocolVersion = .TLSv12
5. Утечка данных через логи
Разработчики часто оставляют print() с конфиденциальными данными в production-сборке.
// ❌ Опасно
print("User token: \(token)")
// ✅ Безопасно — используем условную компиляцию
#if DEBUG
print("Debug: token length = \(token.count)")
#endif
6. Неправильное управление памятью (Retain Cycles)
Утечки памяти могут привести к DoS-атакам. Используйте слабые ссылки в замыканиях.
// ❌ Утечка памяти
class ViewController: UIViewController {
var handler: (() -> Void)?
override func viewDidLoad() {
handler = {
self.view.backgroundColor = .red // Сильная ссылка
}
}
}
// ✅ Безопасно — слабая ссылка
handler = { [weak self] in
self?.view.backgroundColor = .red
}
7. Отсутствие проверки подлинности URL-схем
Мошеннические приложения могут перехватывать URL-схемы вашего приложения.
// ❌ Уязвимо
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
// Обрабатываем любой URL
return true
}
// ✅ Безопасно — проверяем источник
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
guard let sourceApp = options[.sourceApplication] as? String,
sourceApp == "com.trusted.app" else {
return false
}
// Обработка URL
return true
}
8. Использование устаревших API
Старые функции (например, UIWebView) имеют известные уязвимости. Всегда используйте современные альтернативы.
// ❌ Устарело и небезопасно
let webView = UIWebView()
webView.loadHTMLString(htmlString, baseURL: nil)
// ✅ Современный подход
let webView = WKWebView()
webView.loadHTMLString(htmlString, baseURL: nil)
9. Небезопасное кэширование данных
Кэширование может сохранять конфиденциальные данные на устройстве.
// ❌ Опасно — кэшируем всё
let request = URLRequest(url: url)
URLCache.shared.storeCachedResponse(CachedURLResponse(response: response, data: data), for: request)
// ✅ Безопасно — отключаем кэш для чувствительных данных
var request = URLRequest(url: url)
request.cachePolicy = .reloadIgnoringLocalCacheData
10. Отсутствие защиты от Reverse Engineering
Код на Swift можно декомпилировать. Используйте обфускацию и проверки целостности.
// ❌ Лёгкий для анализа код
func checkLicense() -> Bool {
return true
}
// ✅ Безопасно — обфускация и проверка
func checkLicense() -> Bool {
#if !DEBUG
// Проверка целостности бинарника
guard Bundle.main.bundleIdentifier == "com.yourcompany.app" else {
return false
}
#endif
return true
}
Заключение
Безопасность Swift-приложений — это не разовая задача, а постоянный процесс. Начинающим разработчикам важно помнить:
- Никогда не храните пароли и токены в UserDefaults
- Всегда валидируйте пользовательский ввод
- Используйте только HTTPS для сетевых запросов
- Избегайте утечек данных через логи
- Регулярно обновляйте зависимости и API
Применяя эти простые правила на практике, вы значительно снизите риск взлома вашего приложения. Помните: безопасность начинается с первого написанного вами кода. Удачи в разработке!