Android Insecure Storage. Use this skill whenever diffs may introduce security issues on mobile, especially in Kotlin, Java. Actively look for: Android insecure storage includes storing sensitive data in SharedPreferences, SQLite databases, or external storage without encryption, using world-readable... and report findings with high severity expectations and actionable fixes.
securityhighmobileKotlin, JavaAndroid insecure storage includes storing sensitive data in SharedPreferences, SQLite databases, or external storage without encryption, using world-readable file permissions, and missing Android Keystore for cryptographic keys.
// BUGGY CODE — should be detected
val prefs = getSharedPreferences("app_prefs", Context.MODE_PRIVATE)
prefs.edit()
.putString("auth_token", authToken)
.putString("credit_card", cardNumber)
.apply()
Expected finding: High — Sensitive data (auth token, credit card) stored in plaintext SharedPreferences. Rooted devices or ADB backup can extract. Use EncryptedSharedPreferences with Android Keystore.
// BUGGY CODE — should be detected
public void saveUser(String username, String password) {
ContentValues values = new ContentValues();
values.put("username", username);
values.put("password", password); // Plaintext!
db.insert("users", null, values);
}
Expected finding: Critical — Password stored in plaintext SQLite database. Use password hashing (bcrypt/Argon2) for passwords, or EncryptedSharedPreferences for tokens.
// BUGGY CODE — should be detected
object CryptoUtils {
private const val SECRET_KEY = "0123456789abcdef" // Hardcoded!
fun encrypt(data: String): String {
val cipher = Cipher.getInstance("AES/GCM/NoPadding")
val keySpec = SecretKeySpec(SECRET_KEY.toByteArray(), "AES")
cipher.init(Cipher.ENCRYPT_MODE, keySpec)
return Base64.encodeToString(cipher.doFinal(data.toByteArray()), Base64.DEFAULT)
}
}
Expected finding: Critical — Hardcoded encryption key. Key visible via reverse engineering (APK unzip + jadx). Use Android Keystore to generate/store keys: KeyGenerator.getInstance("AES", "AndroidKeyStore").
// CORRECT CODE — should NOT be flagged
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val encryptedPrefs = EncryptedSharedPreferences.create(
context,
"secure_prefs",
masterKey,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
encryptedPrefs.edit()
.putString("auth_token", authToken)
.apply()
Why it's correct: Uses EncryptedSharedPreferences with Android Keystore-backed master key.
// CORRECT CODE — should NOT be flagged
val keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore")
val keyGenSpec = KeyGenParameterSpec.Builder(
"app_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.build()
keyGenerator.init(keyGenSpec)
val secretKey = keyGenerator.generateKey()
Why it's correct: Key generated and stored in Android Keystore hardware-backed security.