Android Security adalah topik penting dalam pengembangan aplikasi Android dengan Kotlin. Memahaminya membantu kita membangun aplikasi yang andal, mudah diuji, dan siap untuk publikasi ke Google Play Store. Pada chapter ini kita akan membongkar konsep inti, melihat contoh kode praktis, dan menutup dengan checklist implementasi.
Ancaman Umum Aplikasi Android #
Buffer overflow tidak lazim di JVM, tetapi banyak celah lain tetap ada: data sensitif di SharedPreferences plain-text, logcat yang bocor, deep link yang tidak divalidasi, sertifikat TLS yang diabaikan, hingga依赖ensi pihak ketiga dengan CVE. OWASP Mobile Top 10 mengelompokkan sepuluh kategori risiko paling umum yang harus dikenali setiap developer Android.
Berikut program demonstrasi konsep-konsep di atas:
// android_security.kt
// Mendemonstrasikan konsep keamanan Android: EncryptedSharedPreferences,
// EncryptedFile, network security config, dan ProGuard/R8 rules. Disimulasikan
// tanpa Android SDK — hanya memetakan kontrak ke struktur data ringkas.
import javax.crypto.Cipher
import javax.crypto.KeyGenerator
import javax.crypto.SecretKey
import javax.crypto.spec.GCMParameterSpec
import javax.crypto.spec.SecretKeySpec
import java.security.SecureRandom
import java.util.Base64
// Mensimulasikan EncryptedSharedPreferences: AES-GCM 256-bit
class EncryptedPrefs(private val masterKey: SecretKey) {
private val store = mutableMapOf<String, ByteArray>() // ciphertext per value
private val ivStore = mutableMapOf<String, ByteArray>()
fun putString(key: String, value: String) {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val iv = ByteArray(12).also { SecureRandom().nextBytes(it) }
cipher.init(Cipher.ENCRYPT_MODE, masterKey, GCMParameterSpec(128, iv))
val encrypted = cipher.doFinal(value.toByteArray())
store[key] = encrypted
ivStore[key] = iv
}
fun getString(key: String, default: String? = null): String? {
val encrypted = store[key] ?: return default
val iv = ivStore[key] ?: return default
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.DECRYPT_MODE, masterKey, GCMParameterSpec(128, iv))
return String(cipher.doFinal(encrypted))
}
fun dumpCiphertext(): Map<String, String> =
store.mapValues { Base64.getEncoder().encodeToString(it.value) }
fun dumpSizes(): Map<String, Int> = store.mapValues { it.value.size }
}
// Mensimulasikan network security config: cleartext traffic policy
object NetworkSecurityConfig {
fun isCleartextAllowed(host: String): Boolean {
val allowlist = setOf("localhost", "10.0.2.2") // dev emulator
return host in allowlist
}
fun isCertificatePinningEnabled(host: String): Boolean =
host.endsWith("example.com") || host.endsWith("android.com")
}
// Mensimulasikan ProGuard/R8 obfuscation
class ProguardRules {
private val keepRules = mutableListOf<String>()
fun keep(rule: String) { keepRules.add(rule) }
fun report(): List<String> = keepRules.toList()
}
fun main() {
// Generate master key (256-bit AES)
val keyGen = KeyGenerator.getInstance("AES").apply { init(256) }
val masterKey: SecretKey = keyGen.generateKey()
val prefs = EncryptedPrefs(masterKey)
println("=== EncryptedSharedPreferences ===")
prefs.putString("auth_token", "Bearer YOUR_API_TOKEN_12345")
prefs.putString("user_id", "user-budi-2026")
println("Ciphertext byte length per key: ${prefs.dumpSizes()}")
println("Dekripsi auth_token: ${prefs.getString("auth_token")}")
println("Dekripsi user_id: ${prefs.getString("user_id")}")
println("\n=== Network security config ===")
listOf("api.example.com", "localhost", "evil.example.org").forEach { host ->
println(" $host: cleartext=${NetworkSecurityConfig.isCleartextAllowed(host)} pinning=${NetworkSecurityConfig.isCertificatePinningEnabled(host)}")
}
println("\n=== ProGuard/R8 keep rules ===")
val rules = ProguardRules()
rules.keep("-keep class com.example.tugasapp.** { *; }")
rules.keep("-keepclassmembers class * { @androidx.room.* <methods>; }")
rules.keep("-keep class kotlinx.serialization.** { *; }")
rules.report().forEach { println(" $it") }
}Output yang Dihasilkan:
=== EncryptedSharedPreferences ===
Ciphertext byte length per key: {auth_token=43, user_id=30}
Dekripsi auth_token: Bearer YOUR_API_TOKEN_12345
Dekripsi user_id: user-budi-2026
=== Network security config ===
api.example.com: cleartext=false pinning=true
localhost: cleartext=true pinning=false
evil.example.org: cleartext=false pinning=false
=== ProGuard/R8 keep rules ===
-keep class com.example.tugasapp.** { *; }
-keepclassmembers class * { @androidx.room.* <methods>; }
-keep class kotlinx.serialization.** { *; }Penyimpanan Terenkripsi #
SharedPreferences tradisional menyimpan data dalam XML plain-text. Untuk data sensitif seperti token, EncryptedSharedPreferences (dari androidx.security:security-crypto) membungkus SharedPreferences dengan enkripsi AES-GCM. File pun dienkripsi dengan AES-GCM. Kunci disimpan di Android Keystore, yang diisolasi per-aplikasi.
Network Security Config #
XML di res/xml/network_security_config.xml mengizinkan kita membatasi domain mana yang boleh menggunakan HTTP (cleartext), dan menambahkan certificate pinning. Sejak Android 9, cleartext default-nya dinonaktifkan.
ProGuard dan R8 #
Build release Android otomatis menjalankan R8 untuk menyingkat nama class/method (obfuscation), menghapus kode tidak terpakai (shrinking), dan mengoptimasi bytecode. R8 meningkatkan keamanan dengan menyulitkan reverse engineering, tetapi reflection bisa rusak. Gunakan @Keep untuk mencegah stripping.
Mitigasi Tambahan #
Flag android:allowBackup="false" mencegah backup data sensitif. android:exported="false" pada komponen internal. Intent validation di deep link. Verifikasi signature Play Integrity API untuk mendeteksi modifikasi.
Referensi Lanjutan #
- Permissions — rujukan permission Android sebagai bagian penting dari praktik keamanan aplikasi
- Guide to app architecture — panduan arsitektur Android yang memuat praktik keamanan data dan komunikasi jaringan
Penutup #
Dengan memahami android security, kita siap membangun aplikasi Android yang lebih matang dan siap produksi. Materi terkait di section lain meliputi coroutines, Compose Multiplatform, dan testing — semuanya saling melengkapi untuk pengembangan Android modern.