Einleitung
Kotlin ist eine moderne Programmiersprache, die aktiv für die Entwicklung von Android-Apps, Serverlösungen (Ktor, Spring Boot) und plattformübergreifenden Projekten eingesetzt wird. Trotz seiner strengen Typisierung und Null-Sicherheit machen Entwickler häufig Fehler, die zu schwerwiegenden Sicherheitslücken führen. In diesem Artikel analysieren wir die Top-10 Schwachstellen von Kotlin-Anwendungen basierend auf OWASP-Standards und zeigen, wie man sie vermeidet.
Der Artikel ist sowohl für Anfänger als auch für erfahrene Kotlin-Entwickler nützlich, die die Sicherheit ihrer Projekte verbessern möchten.
1. SQL-Injection
Problem: Einschleusen von schädlichem SQL-Code über Benutzereingaben.
Beispiel für anfälligen Code:
// Anfällig! Keine String-Konkatenation verwendenfun getUserById(id: String): User? { val query = "SELECT * FROM users WHERE id = '$id'" val result = connection.createStatement().executeQuery(query) return result.toUser()}Lösung: Verwenden Sie parametrisierte Abfragen (PreparedStatement).
// Sicherfun 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: Einschleusen von schädlichem JavaScript-Code in eine Webseite.
Beispiel für anfälligen Code (Ktor):
// Anfällig! Ausgabe nicht escapenget("/search") { val query = call.parameters["q"] ?: "" call.respondHtml { h1 { +"Suchergebnisse für: $query" } // XSS! }}Lösung: Verwenden Sie HTML-Escaping (z.B. kotlinx.html mit Escaping).
// Sicherimport kotlinx.html.escapeHtml
get("/search") { val query = call.parameters["q"] ?: "" val safeQuery = query.escapeHtml() call.respondHtml { h1 { +"Suchergebnisse für: $safeQuery" } }}3. Unsichere Passwortspeicherung
Problem: Speicherung von Passwörtern im Klartext oder mit schwachen Hashes (MD5, SHA-1).
Beispiel für anfälligen Code:
// Anfällig! MD5 ist nicht für Passwörter geeignetfun saveUser(password: String) { val hash = MessageDigest.getInstance("MD5").digest(password.toByteArray()) // hash in der DB speichern}Lösung: Verwenden Sie bcrypt, scrypt oder Argon2.
// Sicher (mit der Bibliothek jbcrypt)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. Leck vertraulicher Daten durch Logs
Problem: Protokollierung von Passwörtern, Tokens, Kartennummern.
Beispiel für anfälligen Code:
// Anfällig! Protokollierung sensibler Datenfun login(username: String, password: String) { log.info("Login-Versuch: username=$username, password=$password")}Lösung: Verwenden Sie Filter oder Maskierung.
// Sicherfun login(username: String, password: String) { log.info("Login-Versuch: username=$username") // password wird nicht protokolliert}5. Unsichere Deserialisierung
Problem: Ausführung von beliebigem Code bei der Deserialisierung ungeprüfter Daten.
Beispiel für anfälligen Code:
// Anfällig! Deserialisierung ohne Prüfungfun process