Swift на передовой: почему безопасность — не опция, а необходимость
Вы пишете на Swift, гордитесь производительностью и красотой кода. Но задумывались ли вы, насколько ваше приложение уязвимо для атак? SQL-инъекции и XSS (межсайтовый скриптинг) — это не просто страшилки из учебников. Это реальные угрозы, которые могут стоить вам данных пользователей, репутации и денег.
В этой статье мы разберём, как защитить Swift-приложение (будь то iOS-клиент или серверная часть на Vapor) от двух самых опасных типов атак. Никакой воды — только практические примеры, готовые решения и живой код. Поехали!
SQL-инъекции в Swift: как хакеры «обманывают» базу данных
Представьте: пользователь вводит в текстовое поле '; DROP TABLE Users; --. Если вы напрямую подставите эту строку в SQL-запрос, база данных выполнит команду удаления. Классика жанра.
В Swift эта уязвимость чаще всего встречается в серверных фреймворках (Vapor, Kitura) или при работе с Core Data и SQLite через небезопасные обёртки.
Опасный пример: как делать НЕ надо
import SQLite
let username = userInput.text // "admin' OR '1'='1"
let query = "SELECT * FROM users WHERE name = '\(username)'"
try db.execute(query) // УГРОЗА! Взломанный запрос
Этот код — красная тряпка для быка. Злоумышленник может получить доступ к любым данным или уничтожить таблицу.
Правильный подход: параметризованные запросы
Золотое правило: никогда не вставляйте пользовательские данные напрямую в строку запроса. Используйте параметризацию.
import SQLite
let db = try Connection("path/to/db.sqlite3")
let users = Table("users")
let name = Expression<String>("name")
let username = userInput.text // "admin' OR '1'='1"
let query = users.filter(name == username) // БЕЗОПАСНО
let result = try db.prepare(query)
Библиотека SQLite.swift сама экранирует спецсимволы. Для Vapor с Fluent используйте модель и фильтры:
import Fluent
import Vapor
struct UserController: RouteCollection {
func login(req: Request) async throws -> String {
let input = try req.content.decode(LoginRequest.self)
// Fluent автоматически параметризует запрос
let user = try await User.query(on: req.db)
.filter(\.$name == input.username)
.first()
return user?.name ?? "Not found"
}
}
Важно: даже если вы используете raw SQL в Vapor, применяйте SQLBind для привязки параметров.
ORM и Core Data: иллюзия безопасности
Многие разработчики считают, что Core Data или Fluent автоматически защищают от инъекций. Это правда — если вы не используете NSExpression с форматированием строк или raw SQL. Но запомните: любой NSPredicate, построенный через строковую интерполяцию, — это риск.
// ОПАСНО
let predicate = NSPredicate(format: "name == %@", userInput) // Безопасно?
// НЕТ! Если userInput содержит '%', может произойти инъекция через подстановку.
Всегда используйте %@ с переменными, а не %K с ключами, и никогда не вставляйте строки через \().
XSS-атаки в Swift: когда злоумышленник внедряет JavaScript
XSS — это атака, при которой вредоносный скрипт выполняется в браузере пользователя. Для iOS-приложений XSS актуален при использовании WKWebView для отображения HTML-контента (например, в ленте новостей или чатах).
Реальный сценарий: чат с HTML-форматированием
Пользователь пишет: Привет! <script>alert('XSS')</script>. Если вы отобразите это сообщение в WebView без обработки — скрипт выполнится.
// ОПАСНО
let html = "<div>\(message.text)</div>"
webView.loadHTMLString(html, baseURL: nil)
Спасательный круг: экранирование HTML
Прежде чем вставлять пользовательский текст в HTML, замените опасные символы:
extension String {
func htmlEscaped() -> String {
var escaped = self
let replacements = [
("&", "&"),
("<", "<"),
(">", ">"),
("\"", """),
("'", "'"),
("/", "/")
]
for (char, entity) in replacements {
escaped = escaped.replacingOccurrences(of: char, with: entity)
}
return escaped
}
}
// БЕЗОПАСНО
let safeText = message.text.htmlEscaped()
let html = "<div>\(safeText)</div>"
webView.loadHTMLString(html, baseURL: nil)
Совет: используйте готовую библиотеку SwiftSoup для парсинга и очистки HTML, если нужно разрешить пользователям форматирование (жирный, курсив).
Лучшая защита: Content Security Policy (CSP)
Для WebView установите политику CSP, которая запрещает выполнение inline-скриптов:
let html = """
<html>
<head>
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'none'">
</head>
<body>\(safeText)</body>
</html>
"""
CSP — это мощный барьер: даже если злоумышленник протащит тег <script>, браузер его не выполнит.
WKWebView: дополнительные настройки
В iOS 14+ можно запретить JavaScript полностью:
let config = WKWebViewConfiguration()
let prefs = WKPreferences()
prefs.javaScriptEnabled = false // Только если не нужен JS
config.preferences = prefs
let webView = WKWebView(frame: .zero, configuration: config)
Если JavaScript необходим (например, для карт), комбинируйте CSP с экранированием ввода.
Сравнение методов защиты: SQL vs XSS
- SQL-инъекции: параметризованные запросы (Fluent, SQLite.swift), ORM, экранирование через библиотеки.
- XSS: экранирование HTML, CSP, отключение JavaScript, валидация ввода на сервере.
- Общее: никогда не доверяйте пользовательскому вводу. Фильтруйте на входе и экранируйте на выходе.
Чек-лист безопасности для Swift-разработчика
Пройдитесь по этим пунктам перед релизом: