Kotlin Application Security: Top 10 Vulnerabilities in Practice
Kotlin confidently holds its position as one of the most popular languages for Android and server application development. However, like any other language, Kotlin is not immune to vulnerabilities. In this article, we will analyze the top 10 most critical security issues that developers face and show how to avoid them in practice.
1. SQL Injection
Despite Kotlin being frequently used with ORM libraries, direct work with SQL queries remains a common practice.
Vulnerable Code:
fun getUser(id: String): User? { val query = "SELECT * FROM users WHERE id = '$id'" val result = connection.createStatement().executeQuery(query) // ...}Secure Version (using PreparedStatement):
fun getUserSafe(id: String): User? { val query = "SELECT * FROM users WHERE id = ?" val stmt = connection.prepareStatement(query) stmt.setString(1, id) val result = stmt.executeQuery() // ...}Conclusion: Never concatenate user input into SQL queries. Use parameterized queries or ORM (Exposed, Hibernate).
2. Cross-Site Scripting (XSS) in Web Applications
When developing server applications with Ktor or Spring Boot, developers often forget to escape output.
Vulnerable Code (Ktor):
get("/user/{name}") { val name = call.parameters["name"] ?: "Guest" call.respondText("<h1>Hello, $name!</h1>", ContentType.Text.Html)}Secure Version:
import org.apache.commons.text.StringEscapeUtils
get("/user/{name}") { val name = call.parameters["name"] ?: "Guest" val safeName = StringEscapeUtils.escapeHtml4(name) call.respondText("<h1>Hello, $safeName!</h1>", ContentType.Text.Html)}Tip: Always escape user input before outputting it in HTML. Use template engines with auto-escaping (e.g., Kotlinx.html).
3. Insecure Password Storage
Storing passwords in plaintext or using weak hashes is one of the most common mistakes.
Wrong Approach:
fun savePassword(password: String) { val hash = password.md5() // Never do this! database.saveHash(hash)}Correct Approach (bcrypt):
import at.favre.lib.crypto.bcrypt.BCrypt
fun savePassword(password: String) { val hash = BCrypt.withDefaults().hashToString(12, password.toCharArray()) database.saveHash(hash)}
fun checkPassword(password: String, storedHash: String): Boolean { return BCrypt.verifyer().verify(password.toCharArray(), storedHash).verified}Important: Use bcrypt, scrypt, or Argon2. Never use MD5 or SHA-1 for storing passwords.
4. Leakage of Confidential Data Through Logs
Developers often log sensitive data without checking what ends up in the output.
Problematic Code:
logger.info("User $userId authorized with token: $token")Secure Version:
logger.info("User $userId authorized successfully")// Or use masking:fun maskToken(token: String): String = token.take(4) + "****" + token.takeLast(4)Rule: Never log passwords, tokens, credit card numbers, or personal data. Use masking libraries (e.g., Logstash).
5. Insecure Deserialization
Kotlin actively uses JSON serialization. Incorrect configuration can lead to RCE attacks.
Vulnerable Code (Jackson):
val mapper = ObjectMapper()val obj = mapper.readValue(userInput, Any::class.j