Kotlin и безопасность: как забыть про SQL-инъекции и XSS

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

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

Начать курс

Ваш код под прицелом: почему безопасность в Kotlin — не опция, а необходимость

Представьте: вы написали идеальное приложение. Красивый интерфейс, молниеносная скорость, чистая архитектура. Но в одной строчке кода прячется «чёрный ход». Злоумышленник вводит в поле поиска ' OR '1'='1 — и ваша база данных открыта как консервная банка. Знакомо? Если нет — отлично. Но лучше быть готовым заранее, чем тушить пожар в продакшене.

Kotlin — мощный и современный язык. Но, как и любой другой инструмент, он не делает ваш код безопасным по умолчанию. SQL-инъекции и XSS (Cross-Site Scripting) остаются в топе OWASP Top 10 уже много лет. И Kotlin-разработчики, которые думают «я использую ORM, мне ничего не грозит», часто оказываются в зоне риска.

В этой статье мы разберём, как превратить ваш Kotlin-код в неприступную крепость. Никакой воды — только практические советы, реальные примеры и лайфхаки, которые вы сможете применить уже сегодня.

SQL-инъекции в Kotlin: анатомия атаки и стратегии защиты

Как выглядит уязвимость (и почему это страшно)

SQL-инъекция возникает, когда пользовательский ввод попадает напрямую в SQL-запрос. Допустим, у вас есть такой код (никогда так не делайте!):

fun getUserById(id: String): User? {
    val query = "SELECT * FROM users WHERE id = '$id'"
    val statement = connection.createStatement()
    val result = statement.executeQuery(query)
    // ...
}

Если злоумышленник передаст id = "1'; DROP TABLE users; --", база данных выполнит: SELECT * FROM users WHERE id = '1'; DROP TABLE users; --'. И ваши пользователи исчезнут. Безвозвратно.

Правило №1: Всегда используйте параметризованные запросы

Это золотой стандарт. Kotlin (и Java) предоставляет PreparedStatement, который разделяет код SQL и данные:

fun getUserByIdSafe(id: String): User? {
    val query = "SELECT * FROM users WHERE id = ?"
    val preparedStatement = connection.prepareStatement(query)
    preparedStatement.setString(1, id)
    val result = preparedStatement.executeQuery()
    // ...
}

База данных получает шаблон запроса отдельно от данных. Даже если в id прилетит вредоносный код, он будет интерпретирован как строка, а не как команда.

Правило №2: Используйте Exposed или другие ORM правильно

Библиотека Exposed — популярный выбор для Kotlin. Она безопасна, если использовать её API правильно. Плохой пример (сырая конкатенация):

// ПЛОХО: уязвимость
val users = transaction {
    exec("SELECT * FROM users WHERE name = '$userInput'")
}

Хороший пример (использование DSL Exposed):

// ХОРОШО: экранирование встроено
val users = transaction {
    Users.select { Users.name eq userInput }
}

В Exposed оператор eq автоматически экранирует значения. Но если вы пишете сырые SQL-запросы через exec — будьте бдительны.

Правило №3: Валидация на входе — ваш второй щит

Даже с параметризованными запросами не стоит пускать в базу всё подряд. Используйте встроенные средства Kotlin для проверки:

fun validateUserId(id: String): Boolean {
    // Проверяем, что id — это UUID или целое число
    return id.matches(Regex("^[a-fA-F0-9-]{36}$")) || 
           id.all { it.isDigit() }
}

Помните: не доверяйте никакому вводу. Даже если он приходит из вашего же фронтенда. Особенно если он приходит из вашего же фронтенда.

XSS-атаки: как не дать JavaScript-коду захватить ваш сайт

Что такое XSS и почему Kotlin не защищает вас автоматически

XSS (Cross-Site Scripting) — это когда злоумышленник внедряет скрипт на вашу страницу. Например, в поле комментария он пишет: <script>alert('Ваши данные украдены')</script>. Если вы выведете этот текст без обработки, скрипт выполнится в браузере каждого пользователя, который откроет страницу.

Kotlin, как язык бэкенда (или мультиплатформы), не обрабатывает HTML-экранирование автоматически. Это ваша задача.

Борьба с XSS: экранирование и шаблонизаторы

Если вы используете Ktor (веб-фреймворк для Kotlin), не выводите пользовательские данные напрямую в HTML-шаблоны. Используйте экранирование:

// ПЛОХО: уязвимо для XSS
fun renderComment(comment: String): String {
    return "<div>$comment</div>"
}

// ХОРОШО: экранируем HTML
fun renderCommentSafe(comment: String): String {
    val escaped = comment
        .replace("&", "&")
        .replace("<", "<")
        .replace(">", ">")
        .replace("\"", """)
        .replace("'", "'")
    return "<div>$escaped</div>"
}

Но лучше использовать готовую библиотеку, например org.owasp.encoder:

// С OWASP Encoder
import org.owasp.encoder.Encode

fun renderCommentOWASP(comment: String): String {
    return "<div>${Encode.forHtml(comment)}</div>"
}

Если вы работаете с шаблонизаторами (например, Kotlinx.html), они часто экранируют вывод по умолчанию. Но проверьте документацию — не все делают это корректно.

Защита от XSS в REST API: Content-Type и заголовки

Если ваше Kotlin-приложение — это REST API, XSS-атаки могут происходить через возвращаемые данные. Установите правильные заголовки:

// Ktor: защита от XSS через заголовки
install(DefaultHeaders) {
    header("X-Content-Type-Options", "nosniff")
    header("X-Frame-Options", "DENY")
    header("Content-Security-Policy", "default-src 'self'")
}

А также всегда указывайте Content-Type: application/json для JSON-ответов. Это предотвратит MIME-сниффинг, когда браузер пытается выполнить JSON как HTML.

Лучшие практики безопасности Kotlin: от зависимостей до архитектуры

1. Держите зависимости в чистоте

Используйте плагины вроде owasp-dependency-check для Gradle. Они сканируют ваши зависимости на известные уязвимости (CVE). Запускайте проверку в CI/CD:

<

Блоги

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