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
        }
    }
}