Back to Cheat Sheets

๐Ÿ’พ Insecure Data Storage

OWASP Mobile Top 10 - M09

CRITICAL RISK

๐Ÿ“‹ What Is It?

Insecure Data Storage occurs when mobile applications store sensitive data in unprotected locations on the device. This includes storing data in plain text in SharedPreferences, NSUserDefaults, SQLite databases, log files, temporary caches, or external storage. Mobile devices can be lost, stolen, or compromised, making secure data storage essential.

M09 OWASP Rank
86% Apps Affected
<20min Time to Extract

โš ๏ธ Common Exploits

  • Physical Access: Access data on lost/stolen device
  • Backup Extraction: Retrieve data from device backups
  • Malware Access: Read app data directories
  • Root/Jailbreak: Access all app sandboxes on compromised device
  • Log File Analysis: Extract sensitive data from logs
  • Cache Inspection: Recover data from temporary storage

๐Ÿ”ด Attack Flow

1. Attacker obtains physical device access
โ†“
2. Connects device via USB, enables root
โ†“
3. Browses /data/data/com.app/shared_prefs/
โ†“
4. Finds auth_token.xml with plaintext token
โ†“
5. BREACH: Full account access!

โŒ Vulnerable Code

// Bad: Plain text in SharedPreferences (Android) public void saveUserData(String username, String password, String token) { SharedPreferences prefs = getSharedPreferences("UserData", MODE_PRIVATE); // VULNERABLE: Storing sensitive data in plain text prefs.edit() .putString("username", username) .putString("password", password) // Never store passwords! .putString("auth_token", token) .putString("credit_card", "4532-1234-5678-9010") .apply(); } // Bad: Unencrypted SQLite (Android) public void saveToDatabase(String ssn, String dob) { SQLiteDatabase db = dbHelper.getWritableDatabase(); // VULNERABLE: Sensitive data in plain SQLite ContentValues values = new ContentValues(); values.put("ssn", ssn); values.put("date_of_birth", dob); values.put("medical_record", "..."); db.insert("users", null, values); } // Bad: External storage (Android) public void saveFile(String data) { // VULNERABLE: World-readable external storage File file = new File(Environment.getExternalStorageDirectory(), "userdata.txt"); FileWriter writer = new FileWriter(file); writer.write(data); // Any app can read this! } // Bad: NSUserDefaults plain text (iOS) func saveCredentials(username: String, password: String) { let defaults = UserDefaults.standard // VULNERABLE: Plain text storage defaults.set(username, forKey: "username") defaults.set(password, forKey: "password") defaults.set(authToken, forKey: "token") defaults.synchronize() } // Bad: Temporary files left behind (iOS) func processImage(_ image: UIImage) { let temp = NSTemporaryDirectory() + "sensitive.jpg" // VULNERABLE: Sensitive data in temp, not cleaned up try? image.jpegData(compressionQuality: 1.0)? .write(to: URL(fileURLWithPath: temp)) // File persists after app closes! } // Bad: Logging sensitive data Log.d("Auth", "User password: " + password); // VULNERABLE! NSLog("Credit card: %@", creditCard); // VULNERABLE!

โœ… Secure Code

// Good: Encrypted SharedPreferences (Android) public void saveUserDataSecurely(String username, String token) { try { // Use EncryptedSharedPreferences (androidx.security) MasterKey masterKey = new MasterKey.Builder(context) .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) .build(); SharedPreferences prefs = EncryptedSharedPreferences.create( context, "secure_prefs", masterKey, EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ); // Data encrypted automatically prefs.edit() .putString("username", username) .putString("auth_token", token) // Encrypted .apply(); } catch (Exception e) { Log.e("Security", "Encryption error", e); } } // Good: SQLCipher for encrypted database (Android) public void saveToEncryptedDatabase(String ssn, String dob) { // Use SQLCipher for encrypted SQLite SQLiteDatabase.loadLibs(context); String password = generateDatabaseKey(); // From Android Keystore SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase( getDatabasePath("secure.db"), password, null ); ContentValues values = new ContentValues(); values.put("ssn", ssn); values.put("dob", dob); db.insert("users", null, values); } // Good: Internal storage only (Android) public void saveFileSecurely(String data) { // Use internal storage (app-private) File file = new File(getFilesDir(), "userdata.txt"); // Encrypt before writing String encrypted = encryptData(data); try (FileWriter writer = new FileWriter(file)) { writer.write(encrypted); } } // Good: iOS Keychain (iOS) func saveCredentialsSecurely(username: String, password: String) { // Use Keychain for credentials let passwordData = password.data(using: .utf8)! let query: [String: Any] = [ kSecClass as String: kSecClassGenericPassword, kSecAttrAccount as String: username, kSecValueData as String: passwordData, kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly ] // Delete old entry if exists SecItemDelete(query as CFDictionary) // Add to Keychain let status = SecItemAdd(query as CFDictionary, nil) if status != errSecSuccess { print("Keychain save failed: \(status)") } } // Good: Secure file operations (iOS) func saveDataSecurely(_ data: Data) { let fileManager = FileManager.default guard let documentsURL = fileManager.urls( for: .documentDirectory, in: .userDomainMask ).first else { return } let fileURL = documentsURL.appendingPathComponent("secure.dat") // Encrypt data before writing if let encryptedData = encrypt(data) { try? encryptedData.write( to: fileURL, options: [.completeFileProtection] // Device-level encryption ) } } // Good: Clean up temporary files func processImageSecurely(_ image: UIImage) { let tempURL = FileManager.default.temporaryDirectory .appendingPathComponent(UUID().uuidString + ".jpg") defer { // Always clean up temp files try? FileManager.default.removeItem(at: tempURL) } if let data = image.jpegData(compressionQuality: 0.8) { try? data.write(to: tempURL) processFile(tempURL) } } // Good: Never log sensitive data if (BuildConfig.DEBUG) { Log.d("Auth", "Login attempt for user"); // No PII } // Production: no logging of credentials/tokens at all

โœ“ Prevention Checklist

  • Use EncryptedSharedPreferences (Android) for sensitive data
  • Store credentials in iOS Keychain or Android Keystore
  • Encrypt SQLite databases with SQLCipher
  • Never store sensitive data on external storage
  • Use internal app storage with encryption
  • Clean up temporary files after use
  • Remove sensitive data from logs
  • Exclude sensitive files from backups
  • Implement data-at-rest encryption
  • Regular security audits of data storage

๐Ÿ” Detection & Tools

Analysis Tools:

MobSF Objection Frida adb iExplorer SQLite Browser

Encryption Libraries:

androidx.security SQLCipher Keychain Services Tink Conceal

How to Test:

  • Extract APK and check /data/data/[app]/
  • Examine SharedPreferences XML files for plain text
  • Open SQLite databases to verify encryption
  • Check external storage for sensitive files
  • Extract and analyze device backups
  • Review logcat output for sensitive data

๐ŸŒ Real-World Breaches

  • Numerous Apps (2020): OAuth tokens stored unencrypted in SharedPreferences
  • Health App (2019): Medical records in plain SQLite database
  • Banking Apps (2018): PINs stored in NSUserDefaults on iOS
  • Social Media (2020): Auth tokens in plaintext logs accessible via backup
  • E-commerce (2019): Payment card data in unencrypted local storage

๐Ÿ“Œ Quick Tips

  • DO NOT store passwords on device
  • DO NOT use plain text for sensitive data
  • DO NOT write sensitive data to external storage
  • DO use platform secure storage (Keychain/Keystore)
  • DO encrypt all sensitive data at rest
  • DO clean up temporary files properly

๐Ÿ“œ Compliance

Related Standards:

  • PCI-DSS Requirement 3.4, 8.2.1
  • GDPR Art. 32 - Encryption at Rest
  • HIPAA ยง164.312(a)(2)(iv)
  • NIST 800-53 SC-28
  • OWASP MASVS MSTG-STORAGE-1 to 15
  • ISO 27001 A.10.1.1