Безопасность Kotlin приложений: топ-10 уязвимостей и защита

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

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

Начать курс

Безопасность Kotlin приложений: топ-10 уязвимостей и методы защиты

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

1. Неправильная обработка nullable-типов (Null Safety)

Одна из сильных сторон Kotlin — система nullable-типов. Однако разработчики часто злоупотребляют оператором !! (not-null assertion), что приводит к NullPointerException при любом неожиданном null-значении.

Пример уязвимости:

fun getUserName(user: User?): String {
    return user!!.name // Если user = null, приложение упадет
}

Как исправить:

fun getUserName(user: User?): String {
    return user?.name ?: "Гость" // Безопасный вызов с дефолтным значением
}

Рекомендация: Используйте ?.let, ?: (elvis) и safe casts. Избегайте !! в production-коде.

2. SQL-инъекции в серверных приложениях

При работе с базами данных через JDBC или Exposed разработчики иногда конкатенируют строки запросов, что открывает путь для SQL-инъекций.

Пример уязвимости:

val id = request.getParameter("id")
val query = "SELECT * FROM users WHERE id = $id" // Опасная конкатенация

Как исправить:

val id = request.getParameter("id")
val stmt = connection.prepareStatement("SELECT * FROM users WHERE id = ?")
stmt.setString(1, id) // Используем параметризованный запрос

Рекомендация: Всегда используйте PreparedStatement или ORM-библиотеки (Exposed, Hibernate) с параметризацией.

3. Небезопасное хранение данных (SharedPreferences)

В Android-приложениях часто хранят токены, пароли и другую конфиденциальную информацию в SharedPreferences в открытом виде.

Пример уязвимости:

val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
prefs.edit().putString("auth_token", "secret123").apply() // Данные в открытом виде

Как исправить:

// Используйте EncryptedSharedPreferences из AndroidX Security
val masterKey = MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build()
val prefs = EncryptedSharedPreferences.create(
    context,
    "secure_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
prefs.edit().putString("auth_token", "secret123").apply()

Рекомендация: Для хранения чувствительных данных используйте EncryptedSharedPreferences или KeyStore.

4. Утечка данных через логи (Logging)

Разработчики часто оставляют в коде логи, которые выводят пароли, токены или персональные данные пользователей.

Пример уязвимости:

Log.d("Auth", "User password: $password") // Пароль попадает в логи

Как исправить:

// Используйте Timber или кастомный логгер с фильтрацией
Log.d("Auth", "User authenticated successfully") // Не логируем чувствительные данные

Рекомендация: Включите ProGuard/R8 для удаления логов в release-сборке и используйте библиотеки с маскировкой данных (например, Timber).

5. Небезопасные WebView (Android)

WebView — частая точка входа для XSS-атак и утечки данных, если включен JavaScript и не настроены фильтры.

Пример уязвимости:

webView.settings.javaScriptEnabled = true
webView.loadUrl("file:///android_asset/malicious.html") // Уязвимость к XSS

Как исправить:

webView.settings.javaScriptEnabled = false // Отключаем JS, если не нужен
webView.webViewClient = object : WebViewClient() {
    override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
        // Проверяем URL на белый список
        return !isAllowedUrl(request.url.toString())
    }
}

Рекомендация: Отключайте JavaScript, если он не требуется, и используйте белый список разрешенных URL.

6. Отсутствие проверки прав доступа (Android Permissions)

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

Пример уязвимости:

// Запрос разрешения без проверки, предоставлено ли оно
val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
startActivityForResult(cameraIntent, REQUEST_CODE)

Как исправить:

if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 
    == PackageManager.PERMISSION_GRANTED) {
    // Только после проверки запускаем камеру
    val cameraIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
    startActivityForResult(cameraIntent, REQUEST_CODE)
} else {
    // Запрашиваем разрешение
    ActivityCompat.requestPermissions(this, 
        arrayOf(Manifest.permission.CAMERA), REQUEST_CODE)
}

Рекомендация: Всегда проверяйте разрешения перед доступом к конфиденциальным API.

7. Небезопасное шифрование (Crypto)

Использование устаревших алгоритмов (MD5, SHA1) или неправильная генерация ключей делает шифрование бесполезным.

Пример уязвимости:

val hash = MessageDigest.getInstance("MD5").digest(password.toByteArray()) // MD5 устарел

Как исправить:

val hash = MessageDigest.getInstance("SHA-256").digest(password.toByteArray())
// Или используйте bcrypt/argon2 для хеши                

Блоги

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