Security is part of every app that handles accounts, money or personal data. The goal is simple: protect the user's data on the device and on the network, and assume that a determined person can inspect your APK. This lesson lists the practical steps.
Login, passwords and OTP
- Validate on the device for good feedback (email shape, minimum length), and on the server for real. Never rely only on the app.
- Never store a password. Store a short-lived token, and ask for the password again for sensitive actions.
- Rate-limit and lock out repeated wrong attempts on the server.
- OTP (one-time password) codes should expire quickly and be single-use. Let the user request a new one, but not endlessly.
Android Keystore and encrypted storage
The Android Keystore stores cryptographic keys in protected hardware where available, so your app can use a key but cannot read it out. Create an AES key in the Keystore, encrypt the token with it, and store the encrypted bytes. The older EncryptedSharedPreferences does this for you but is deprecated in recent versions of the security library, so check the current guidance; a DataStore plus Keystore encryption (for example through the Tink library) is the modern pattern.
fun createKey(alias: String): SecretKey {
val generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
generator.init(
KeyGenParameterSpec.Builder(alias, KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.build()
)
return generator.generateKey()
}
fun encrypt(key: SecretKey, plain: ByteArray): Pair<ByteArray, ByteArray> {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
cipher.init(Cipher.ENCRYPT_MODE, key)
return cipher.iv to cipher.doFinal(plain) // store the iv with the data
}
Do not invent your own cryptography. Use well-known libraries and the algorithms above, and keep the key in the Keystore, never in code.
Biometric authentication
The BiometricPrompt API shows the system fingerprint or face dialog, so you never handle biometric data yourself. Use it to unlock the app or to approve an action, and ideally tie it to a Keystore key that needs authentication (setUserAuthenticationRequired(true)). Always offer a fallback such as device credentials.
val info = BiometricPrompt.PromptInfo.Builder()
.setTitle("Unlock")
.setSubtitle("Confirm it is you")
.setAllowedAuthenticators(BiometricManager.Authenticators.BIOMETRIC_STRONG or BiometricManager.Authenticators.DEVICE_CREDENTIAL)
.build()
val prompt = BiometricPrompt(activity, ContextCompat.getMainExecutor(activity),
object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { unlock() }
})
prompt.authenticate(info)
R8, obfuscation and tampering
R8 shrinks and renames code in release builds (isMinifyEnabled = true). That makes reverse engineering harder, but it is not security: assume attackers can read your logic. Root detection and integrity checks (Play Integrity API) can raise the effort for fraud-prone apps, but they can be bypassed, so enforce the important rules on the server.
OWASP Mobile ideas and best practice
- Improper credential usage: no keys or passwords in the app.
- Insecure communication: HTTPS only, validate certificates.
- Insecure data storage: sensitive data encrypted, and not in logs, backups or screenshots (
FLAG_SECUREon sensitive screens). - Insufficient input validation: validate everything, including deep link and intent data.
- Insecure authentication: short-lived tokens, server-side checks.
- Keep dependencies updated, request only the permissions you need, and mark components
exported=falseunless they must be public.
Your task
- Encrypt a token with a Keystore key and decrypt it again.
- Add a biometric unlock screen to one project with a device credential fallback.
- Turn on R8 for a release build and confirm the app still works.
- Review one project against the list above and write down three fixes.
How to check it worked
- No secret appears in the source or logs.
- Biometric login falls back correctly.
- You can say why obfuscation is not real security.