Introduction
Kotlin is a modern programming language actively used for developing Android applications, server-side solutions (Ktor, Spring Boot) and cross-platform projects. Despite its strong typing and null-safety, developers often make mistakes that lead to serious vulnerabilities. In this article, we will analyze the top 10 vulnerabilities of Kotlin applications, based on OWASP standards, and show how to avoid them.
This article will be useful for both beginner and experienced Kotlin developers looking to improve the security level of their projects.
1. SQL Injection
Problem: Injection of malicious SQL code through user input.
Example of vulnerable code:
// Vulnerable! Do not use string concatenationfun getUserById(id: String): User? { val query = "SELECT * FROM users WHERE id = '$id'" val result = connection.createStatement().executeQuery(query) return result.toUser()}Solution: Use parameterized queries (PreparedStatement).
// Safefun getUserById(id: String): User? { val query = "SELECT * FROM users WHERE id = ?" val preparedStatement = connection.prepareStatement(query) preparedStatement.setString(1, id) val result = preparedStatement.executeQuery() return result.toUser()}2. Cross-Site Scripting (XSS)
Problem: Insertion of malicious JavaScript code into a web page.
Example of vulnerable code (Ktor):
// Vulnerable! Do not escape outputget("/search") { val query = call.parameters["q"] ?: "" call.respondHtml { h1 { +"Search results for: $query" } // XSS! }}Solution: Use HTML escaping (e.g., kotlinx.html with escaping).
// Safeimport kotlinx.html.escapeHtml
get("/search") { val query = call.parameters["q"] ?: "" val safeQuery = query.escapeHtml() call.respondHtml { h1 { +"Search results for: $safeQuery" } }}3. Insecure Password Storage
Problem: Storing passwords in plain text or using weak hashes (MD5, SHA-1).
Example of vulnerable code:
// Vulnerable! MD5 is not suitable for passwordsfun saveUser(password: String) { val hash = MessageDigest.getInstance("MD5").digest(password.toByteArray()) // save hash in DB}Solution: Use bcrypt, scrypt, or Argon2.
// Safe (using jbcrypt library)import org.mindrot.jbcrypt.BCrypt
fun hashPassword(password: String): String { return BCrypt.hashpw(password, BCrypt.gensalt(12))}
fun checkPassword(password: String, hashed: String): Boolean { return BCrypt.checkpw(password, hashed)}4. Leakage of Confidential Data via Logs
Problem: Logging passwords, tokens, card numbers.
Example of vulnerable code:
// Vulnerable! Logging sensitive datafun login(username: String, password: String) { log.info("Login attempt: username=$username, password=$password")}Solution: Use filters or masking.
// Safefun login(username: String, password: String) { log.info("Login attempt: username=$username") // password is not logged}5. Insecure Deserialization
Problem: Execution of arbitrary code during deserialization of untrusted data.
Example of vulnerable code:
// Vulnerable! Deserialization without validationfun process