Безопасная аутентификация в Kotlin: гид для разработчика

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

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

Начать курс

Введение: почему "своя велосипедная" аутентификация — это риск?

Представьте: вы пишете приложение на Kotlin — будь то Android-клиент или бэкенд на Ktor/Spring Boot. Вы быстро накидываете логин-форму, сохраняете пароль в SharedPreferences и радуетесь, что всё работает. А через неделю получаете сообщение: "Ваше приложение взломали, данные утекли". Знакомо? К сожалению, это классика.

Безопасная аутентификация в Kotlin — это не просто "проверить пароль". Это целая экосистема: хеширование, токены, защита от подделки запросов, безопасное хранение секретов. В этой статье мы разберём, как не допустить типичных ошибок и построить надёжную систему входа. Поехали!

1. Хеширование паролей: bcrypt и его друзья

Первое правило безопасности: никогда не храните пароли в открытом виде. Даже если вы используете HTTPS, база данных может быть скомпрометирована. Ваша задача — хранить только хеш.

Почему MD5 и SHA — зло?

Эти алгоритмы быстрые. А быстрые хеши — мечта взломщика: он может перебрать миллиарды комбинаций в секунду. Вместо них используйте bcrypt, scrypt или Argon2. Они намеренно медленные и устойчивы к атакам на GPU.

Пример на Kotlin с использованием библиотеки jbcrypt:

import org.mindrot.jbcrypt.BCrypt

fun hashPassword(password: String): String {
    val salt = BCrypt.gensalt(12) // cost factor 12 — оптимально
    return BCrypt.hashpw(password, salt)
}

fun checkPassword(password: String, hashed: String): Boolean {
    return BCrypt.checkpw(password, hashed)
}

Важно: соль генерируется автоматически и встраивается в хеш. Вам не нужно хранить её отдельно.

2. JWT: лёгкая и безопасная аутентификация

JSON Web Token (JWT) — стандарт для передачи утверждений (claims) между сторонами. В Kotlin вы можете использовать библиотеку java-jwt (Auth0) или jsonwebtoken (jjwt).

Генерация JWT на Kotlin

import io.jsonwebtoken.Jwts
import io.jsonwebtoken.SignatureAlgorithm
import io.jsonwebtoken.security.Keys
import java.util.Date

fun generateToken(userId: String, secretKey: String): String {
    val key = Keys.hmacShaKeyFor(secretKey.toByteArray())
    return Jwts.builder()
        .setSubject(userId)
        .setIssuedAt(Date())
        .setExpiration(Date(System.currentTimeMillis() + 3600_000)) // 1 час
        .signWith(key, SignatureAlgorithm.HS256)
        .compact()
}

Проверка токена

fun validateToken(token: String, secretKey: String): String? {
    return try {
        val key = Keys.hmacShaKeyFor(secretKey.toByteArray())
        val claims = Jwts.parserBuilder()
            .setSigningKey(key)
            .build()
            .parseClaimsJws(token)
            .body
        claims.subject // возвращаем userId
    } catch (e: Exception) {
        null // токен невалиден или истёк
    }
}

Совет: не храните секретный ключ в коде. Используйте переменные окружения или Vault.

3. Безопасное хранение токенов на Android

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

Используйте EncryptedSharedPreferences (из AndroidX Security) или Keystore:

import androidx.security.crypto.EncryptedSharedPreferences
import androidx.security.crypto.MasterKey

fun saveToken(context: Context, token: String) {
    val masterKey = MasterKey.Builder(context)
        .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
        .build()

    val sharedPreferences = EncryptedSharedPreferences.create(
        context,
        "secure_prefs",
        masterKey,
        EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
        EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
    )
    sharedPreferences.edit().putString("auth_token", token).apply()
}

Для веб-приложений используйте HttpOnly cookies — они недоступны для JavaScript и защищены от XSS-атак.

4. OAuth2 и Social Login: делегируем безопасность

Зачем писать свою аутентификацию, если можно доверить её Google, GitHub или VK? OAuth2 — стандарт де-факто для авторизации через сторонние сервисы.

В Kotlin (Spring Boot) подключить OAuth2 просто:

spring.security.oauth2.client.registration.google.client-id=ВАШ_CLIENT_ID
spring.security.oauth2.client.registration.google.client-secret=ВАШ_SECRET

А на стороне кода:

import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity

@EnableWebSecurity
class SecurityConfig : WebSecurityConfigurerAdapter() {
    override fun configure(http: HttpSecurity) {
        http
            .oauth2Login()
            .and()
            .authorizeRequests()
            .antMatchers("/public/**").permitAll()
            .anyRequest().authenticated()
    }
}

Плюсы: не нужно хранить пароли, пользователи доверяют знакомому провайдеру, поддержка MFA (многофакторной аутентификации) из коробки.

5. Защита от распространённых атак

SQL-инъекции

В Kotlin с JPA или Exposed вы защищены, если используете параметризованные запросы. Но если пишете raw SQL — всегда используйте ? вместо конкатенации строк:

// Плохо
val query = "SELECT * FROM users WHERE login = '$login'"

// Хорошо
val prepared = connection.prepareStatement("SELECT * FROM users WHERE login = ?")
prepared.setString(1, login)

CSRF (межсайтовая подделка запроса)

Для веб-приложений включите CSRF-защиту (Spring Security делает это по умолчанию). Для REST API используйте JWT — он защищает от CSRF, так как токен не передаётся автоматически через cookies.

Брутфорс и rate limiting

Ограничьте количество попыток входа с одного IP:

import io.github.bucket4j.Bandwidth
import io.github.bucket4j.Bucket
import java.time.Duration

fun createLoginBucket(): Bucket {
    val limit = Bandwidth.simple(10, Duration.ofMinutes(1))
    return Bucket.builder().addLimit(limit).build()
}

6. OWASP Top 10 и Kotlin: что проверить в первую очередь

Рекомендую пройтись по чек-листу OWASP для веб-приложений. Вот ключевые пункты для Kotlin-проекта:

  • A1: Broken Access Control — проверяйте права на

Блоги

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