Why Security Matters in KMP
A single vulnerability can lead to:
- Data Breaches: Leaked user credentials, payment info, or personal data.
- Legal Penalties: Fines under GDPR, CCPA, or other privacy laws.
- Reputation Damage: Loss of user trust after security incidents.
KMP-Specific Risks:
- Platform-Specific Flaws: Misconfigured iOS Keychain or Android Keystore.
- Shared Code Exploits: A vulnerability in shared code impacts all platforms.
- Inconsistent Updates: Security patches might lag on certain platforms.
Real-World Impact:
- A travel app using KMP faced a breach when unencrypted SQLite data on iOS was extracted from jailbroken devices.
- A fintech app avoided MITM (man-in-the-middle) attacks by implementing SSL pinning across Android and iOS.
1. Fortify API Communication
1.1 Enforce HTTPS Everywhere
Why It Matters: HTTP exposes data to eavesdropping and tampering.
Implementation:
// Ktor client configuration
val client = HttpClient(CIO) {
expectSuccess = true // Throw exceptions for non-2xx responses
}
// Usage
suspend fun fetchData() = client.get("https://api.secure.com/data")
Code language: JavaScript (javascript)
Best Practices:
- Use HSTS (HTTP Strict Transport Security) headers on servers.
- Redirect HTTP to HTTPS via server-side rules.
1.2 SSL Pinning
What It Solves: Prevents certificate spoofing in MITM attacks.
Implementation with Ktor:
HttpClient(CIO) {
engine {
https.pinning = CertificatePinner.Builder()
.add("api.secure.com", "sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=")
.build()
}
}
Code language: JavaScript (javascript)
Step-by-Step:
- Extract your server’s public key hash:
openssl s_client -connect api.secure.com:443 | openssl x509 -pubkey | openssl pkey -pubin -outform der | openssl dgst -sha256 -binary | openssl enc -base64
- Update the
CertificatePinner
with the hash.
Tools: Use SSLPinner for automated pinning.
2. Protect Sensitive Data
2.1 Encrypted Storage
Cross-Platform Solution:
// Using multiplatform-settings-encrypted
val settings = EncryptedSettings(Settings())
settings.putString("auth_token", "JWT_TOKEN")
// Retrieve securely
val token = settings.getString("auth_token")
Code language: JavaScript (javascript)
Platform-Specific Best Practices:
- Android: Use
EncryptedSharedPreferences
orBiometricPrompt
for key storage. - iOS: Store secrets in Keychain with
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnly
. - Web: Avoid
localStorage
for sensitive data; use HTTP-only cookies.
2.2 Secure Key Management
Never Hardcode Secrets:
- Use environment variables for API keys:
val apiKey = System.getenv("API_KEY")
- Leverage cloud services like AWS Secrets Manager or Azure Key Vault.
Android Keystore Example:
val keyStore = KeyStore.getInstance("AndroidKeyStore")
keyStore.load(null)
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
keyGenerator.init(KeyGenParameterSpec.Builder("alias", KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT).build())
keyGenerator.generateKey()
Code language: JavaScript (javascript)
3. Authentication Best Practices
3.1 OAuth2 & JWT
Implementation Flow:
- Login:
suspend fun login(username: String, password: String): Token { return HttpClient().post("https://auth.secure.com/token") { headers { append("Authorization", "Basic ${base64Encode("$username:$password")}") } }.body() }
- Token Refresh:
suspend fun refreshToken(refreshToken: String): Token { return HttpClient().post("https://auth.secure.com/refresh") { headers { append("Authorization", "Bearer $refreshToken") } }.body() }
Best Practices:
- Set short-lived access tokens (e.g., 15 minutes).
- Store refresh tokens securely and revoke them on logout.
3.2 Biometric Authentication
Android Implementation:
val biometricPrompt = BiometricPrompt(
activity,
ContextCompat.getMainExecutor(activity),
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
// Unlock sensitive data
}
}
)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
.setTitle("Login")
.setSubtitle("Use your fingerprint to authenticate")
.setNegativeButtonText("Cancel")
.build()
biometricPrompt.authenticate(promptInfo)
Code language: JavaScript (javascript)
iOS Implementation:
val context = LAContext()
context.evaluatePolicy(
LAPolicy.LAPolicyDeviceOwnerAuthentication,
"Authenticate to access data",
completion = { success, error ->
if (success) { /* Proceed */ }
}
)
Code language: JavaScript (javascript)
4. Prevent Reverse Engineering
4.1 Code Obfuscation
Android (ProGuard/R8):
// app/build.gradle
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
Code language: JavaScript (javascript)
iOS (Bitcode):
Enable Bitcode in Xcode under Build Settings > Enable Bitcode.
Advanced Tools:
- DexGuard: Enhanced obfuscation for Android.
- LLVM Obfuscator: For C/C++ interop code.
4.2 Tamper Detection
Detect Rooted/Jailbroken Devices:
fun isDeviceSecure(): Boolean {
// Android
val isRooted = File("/system/app/Superuser.apk").exists()
// iOS
val isJailbroken = NSFileManager.defaultManager.fileExistsAtPath("/Applications/Cydia.app")
return !isRooted && !isJailbroken
}
Code language: JavaScript (javascript)
Response Strategy:
- Log the event and notify servers.
- Restrict app functionality or force logout.
5. Secure Coding Practices
5.1 Input Validation
Prevent SQL Injection:
// SQLDelight parameterized query
val query = UserQueries.selectById(userId)
Code language: JavaScript (javascript)
Sanitize User Input:
fun sanitizeInput(input: String): String {
return input.replace("<script>", "", ignoreCase = true)
}
Code language: JavaScript (javascript)
5.2 Logging Discipline
Strip Logs in Release Builds:
-assumenosideeffects class android.util.Log {
public static int d(...);
public static int w(...);
}
Code language: PHP (php)
Secure Logging:
if (BuildConfig.DEBUG) {
Log.d("Auth", "Token: $token") // Debug only
}
Code language: JavaScript (javascript)
6. Ongoing Security Maintenance
- Automated Dependency Updates:
Use Dependabot or Renovate to patch vulnerabilities. - Penetration Testing:
Hire ethical hackers to simulate attacks. Tools: Burp Suite, OWASP ZAP. - Monitor Threats:
Use MobSF for static analysis and Google Play App Signing for tamper detection.
Key Takeaways
- HTTPS & SSL Pinning: Mandatory for secure API communication.
- Encrypted Storage: Use platform-native solutions for keys/secrets.
- OAuth2/JWT: Prefer token-based auth with short expiration times.
- Code Obfuscation: Deter reverse engineering with ProGuard/R8.
- Input Validation: Sanitize all user inputs to prevent injection.
- Internal Links: A Comprehensive Guide to Cross-Platform Development, How to Switch Ruby Versions on Mac (M1, M2, M3, M4 Guide), Mastering Lifecycle & Performance in Compose Multiplatform | Cross-Platform Optimization Guide
- External Links: OWASP Mobile Top 10, Ktor Documentation.