Swift-безопасность: SQL-инъекции и XSS — как защитить код

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

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-разработчика

Пройдитесь по этим пунктам перед релизом:

Блоги

Рекомендаций по книгам