Introduction
Kotlin est un langage de programmation moderne, activement utilisé pour le développement d'applications Android, de solutions serveur (Ktor, Spring Boot) et de projets multiplateformes. Malgré son typage strict et sa null-safety, les développeurs commettent souvent des erreurs qui entraînent des vulnérabilités graves. Dans cet article, nous allons analyser le top 10 des vulnérabilités des applications Kotlin, basées sur les normes OWASP, et montrer comment les éviter.
Cet article sera utile aussi bien aux développeurs Kotlin débutants qu'expérimentés souhaitant améliorer le niveau de sécurité de leurs projets.
1. Injections SQL (SQL Injection)
Problème : Injection de code SQL malveillant via une saisie utilisateur.
Exemple de code vulnérable :
// Vulnérable ! N'utilisez pas la concaténation de chaînesfun getUserById(id: String): User? { val query = "SELECT * FROM users WHERE id = '$id'" val result = connection.createStatement().executeQuery(query) return result.toUser()}Solution : Utilisez des requêtes paramétrées (PreparedStatement).
// Sécuriséfun 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. Scripting inter-sites (XSS)
Problème : Insertion de code JavaScript malveillant dans une page web.
Exemple de code vulnérable (Ktor) :
// Vulnérable ! N'échappez pas la sortieget("/search") { val query = call.parameters["q"] ?: "" call.respondHtml { h1 { +"Résultats de recherche pour : $query" } // XSS ! }}Solution : Utilisez l'échappement HTML (par exemple, kotlinx.html avec échappement).
// Sécuriséimport kotlinx.html.escapeHtml
get("/search") { val query = call.parameters["q"] ?: "" val safeQuery = query.escapeHtml() call.respondHtml { h1 { +"Résultats de recherche pour : $safeQuery" } }}3. Stockage non sécurisé des mots de passe
Problème : Stockage des mots de passe en clair ou avec des hachages faibles (MD5, SHA-1).
Exemple de code vulnérable :
// Vulnérable ! MD5 n'est pas adapté aux mots de passefun saveUser(password: String) { val hash = MessageDigest.getInstance("MD5").digest(password.toByteArray()) // enregistrer le hash dans la BD}Solution : Utilisez bcrypt, scrypt ou Argon2.
// Sécurisé (avec la bibliothèque 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. Fuite de données confidentielles via les logs
Problème : Journalisation des mots de passe, jetons, numéros de carte.
Exemple de code vulnérable :
// Vulnérable ! Nous journalisons des données sensiblesfun login(username: String, password: String) { log.info("Tentative de connexion : username=$username, password=$password")}Solution : Utilisez des filtres ou du masquage.
// Sécuriséfun login(username: String, password: String) { log.info("Tentative de connexion : username=$username") // password n'est pas journalisé}5. Désérialisation non sécurisée
Problème : Exécution de code arbitraire lors de la désérialisation de données non vérifiées.
Exemple de code vulnérable :
// Vulnérable ! Désérialisation sans vérificationfun process