M2: Insecure Data Storage
M2: Insecure Data Storage
This vulnerability occurs when sensitive data is stored insecurely on the mobile device. Attackers with physical access or malware can easily retrieve this data.
Common Storage Locations at Risk:
- SQL databases without encryption
- Log files
- XML/plist files
- Binary data stores
- Cookie stores
- SD cards/external storage
- Cloud synced folders
Detection and Mitigation:
// iOS - Comprehensive secure storage implementation
class SecureDataManager {
// VULNERABLE: Multiple insecure storage examples
class InsecureStorage {
func demonstrateVulnerabilities() {
// Bad: Storing in plist
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
let documentsDirectory = paths[0]
let path = (documentsDirectory as NSString).appendingPathComponent("sensitive.plist")
let sensitiveData = ["password": "secret123", "apiKey": "abc123"]
(sensitiveData as NSDictionary).write(toFile: path, atomically: true)
// Bad: Logging sensitive data
print("User password: \(sensitiveData["password"]!)")
// Bad: Storing in UserDefaults
UserDefaults.standard.set("secret_token", forKey: "authToken")
// Bad: Caching sensitive data
URLCache.shared.storeCachedResponse(
CachedURLResponse(response: URLResponse(), data: "sensitive".data(using: .utf8)!),
for: URLRequest(url: URL(string: "https://api.example.com/user")!)
)
}
}
// SECURE: Proper data storage with encryption
class SecureStorage {
private let encryptionKey = SymmetricKey(size: .bits256)
func storeSensitiveData(_ data: Data, identifier: String) throws {
// Encrypt data before storage
let sealedBox = try AES.GCM.seal(data, using: encryptionKey)
let encryptedData = sealedBox.combined!
// Store in Keychain (most secure)
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: identifier,
kSecValueData as String: encryptedData,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
SecItemDelete(query as CFDictionary)
let status = SecItemAdd(query as CFDictionary, nil)
guard status == errSecSuccess else {
throw StorageError.keychainError(status)
}
// Alternative: Store in encrypted file
try storeEncryptedFile(encryptedData, identifier: identifier)
}
private func storeEncryptedFile(_ data: Data, identifier: String) throws {
let documentsPath = FileManager.default.urls(for: .documentDirectory,
in: .userDomainMask).first!
let fileURL = documentsPath.appendingPathComponent("\(identifier).encrypted")
// Write with data protection
try data.write(to: fileURL, options: [.atomic, .completeFileProtection])
// Exclude from backup
var resourceValues = URLResourceValues()
resourceValues.isExcludedFromBackup = true
try fileURL.setResourceValues(resourceValues)
}
// Secure database implementation
func setupSecureDatabase() -> NSPersistentContainer {
let container = NSPersistentContainer(name: "DataModel")
let storeURL = container.persistentStoreDescriptions.first!.url!
// Enable encryption
container.persistentStoreDescriptions.first?.setOption(
FileProtectionType.complete.rawValue,
forKey: NSPersistentStoreFileProtectionKey
)
// Enable SQLite encryption
container.persistentStoreDescriptions.first?.setOption(
["cipher": "aes-256-cbc", "kdf_iter": "64000"],
forKey: NSSQLitePragmasOption
)
return container
}
}
}
// Android - Secure data storage implementation
class SecureDataStorageManager(private val context: Context) {
// VULNERABLE: Common insecure storage patterns
class InsecurePatterns {
fun demonstrateVulnerabilities(context: Context) {
// Bad: External storage without encryption
val file = File(context.getExternalFilesDir(null), "sensitive.txt")
file.writeText("password=secret123")
// Bad: World-readable files
context.openFileOutput("data.txt", Context.MODE_WORLD_READABLE).use {
it.write("sensitive data".toByteArray())
}
// Bad: Logging sensitive information
Log.d("Auth", "User token: abc123xyz")
// Bad: SQLite without encryption
val db = context.openOrCreateDatabase("app.db", Context.MODE_PRIVATE, null)
db.execSQL("CREATE TABLE users (id INTEGER, password TEXT)")
db.execSQL("INSERT INTO users VALUES (1, 'plaintext_password')")
}
}
// SECURE: Comprehensive secure storage
class SecureImplementation(private val context: Context) {
fun storeSecureData(key: String, value: ByteArray) {
// Option 1: Android Keystore + Encrypted file
val masterKey = MasterKey.Builder(context)
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
.build()
val encryptedFile = EncryptedFile.Builder(
context,
File(context.filesDir, "$key.enc"),
masterKey,
EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB
).build()
encryptedFile.openFileOutput().use { output ->
output.write(value)
}
// Option 2: Room database with SQLCipher
setupEncryptedDatabase()
}
private fun setupEncryptedDatabase(): RoomDatabase {
val passphrase = generateDatabaseKey()
val factory = SupportFactory(passphrase)
return Room.databaseBuilder(
context,
AppDatabase::class.java,
"encrypted_app.db"
)
.openHelperFactory(factory)
.build()
}
private fun generateDatabaseKey(): ByteArray {
val keyGenerator = KeyGenerator.getInstance(
KeyProperties.KEY_ALGORITHM_AES,
"AndroidKeyStore"
)
val keyGenSpec = KeyGenParameterSpec.Builder(
"db_key",
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT
)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setKeySize(256)
.build()
keyGenerator.init(keyGenSpec)
return keyGenerator.generateKey().encoded
}
}
}