
BLOG
BLOG
Mobile apps often handle sensitive data daily, such as credentials, tokens, health records, financial information, and personal identifiers that attackers seek to exploit.
On iOS, developers sometimes assume local data storage is inherently secure because of sandboxing and built-in Apple protections. This assumption is flawed.
Poorly implemented storage practices can expose critical data, leading to severe privacy and security incidents.
This article examines
Insecure data storage refers to storing sensitive information on a device without strong protection.
Attackers with device access—physical theft, malware, or remote compromise—can extract this data using forensic tools, jailbreak techniques, or even basic device backups. Once retrieved, the data may allow account takeover, identity theft, or further lateral movement.
The primary contributors to insecure storage are:
In iOS applications, data can be stored in various locations, including:
NSUserDefaults provides a convenient way for iOS applications to store user preferences and lightweight data, but it has significant security vulnerabilities if misused.
Many developers inadvertently store sensitive information, such as authentication credentials, API keys, or personally identifiable information (PII), in NSUserDefaults, not realizing that this data is stored in an unencrypted property list or plist file located in the application's ”/Library/Preference/“ directory.
Moreover, the data stored in NSUserDefaults is not protected by iOS keychain services, making it vulnerable to unauthorized access and manipulation, particularly on jailbroken devices.
If an attacker were to access this file, they could potentially steal user credentials, modify application settings, or bypass established security protocols.
Tools like iExplorer or simple device backups can extract this data within seconds.
Sensitive data can be exposed in a keychain with inappropriate accessibility settings. While Apple’s Keychain is the recommended place for storing sensitive credentials, improper configuration undermines its security.
Developers often use kSecAttrAccessibleAlways, which keeps items available even when the device is locked, creating exposure. Weak access controls or misconfigurations in group sharing can also allow data leakage between apps.
SQLite is an open-source SQL database that stores data in a text file on a device. It supports all the features of a relational database and is widely used for structured app data.
Storing sensitive data, such as user credentials, session tokens, and API keys, can expose this data to other applications or a superuser, as no authentication is required to access it.
Unencrypted SQLite files are easily extracted using forensic tools. Metadata, query history, and complete records—including PII or financial details—can be recovered.
The SQLite engine does not have built-in security to protect databases. Instead, it relies on its environment, such as the operating system, to provide security for database content.
All data being stored in the database must be verified and secured.
Core Data is a framework for managing the model layer of objects in your application. It serves as a means for applications to store data for use in subsequent stages of operation.
CoreData is often backed by SQLite. These databases do not have built-in encryption features, meaning all information is stored in plain text within the files.
As a result, if an application saves user credentials or other sensitive personal information in this database, it is vulnerable to unauthorized access. Attackers can traverse schema and entity relationships, reconstructing user activity or private content.
The Realm databases are stored as files with the .realm extension, and they are located within the file system at the specific path:
/Application/Containers/Data/Application/<AppID>/Documents/default.realm
This path is part of the app's sandboxed environment on iOS devices, which is designed to isolate app data from other applications.
However, on jailbroken devices, this sandboxing can be bypassed, allowing malicious actors to navigate the file system and access these .realm files directly.
Realm databases store data in a binary format, but without enabling built-in encryption, the files remain unprotected. Attackers with file system access can parse these files.
Developers may neglect Realm’s encryptionKey configuration, leaving the database equivalent to plaintext.
YapDatabase is a high-performance, key-value database for iOS and macOS applications, often used as an alternative to Core Data or Realm. YapDatabase offers robust features, including caching, automatic serialization, and multithreading.
As a result, if an application uses this database to store user credentials or other sensitive information, such data could be at risk of being accessed by unauthorized third parties.
By default, the Yap database stores data in plain text. Sensitive objects stored here can be extracted directly.
Without explicit encryption layers, Yap provides no inherent guarantees of confidentiality.
Couchbase Lite is a compact, embedded, document-oriented (NoSQL) database engine that supports synchronization. Couch databases serve as storage solutions for applications, allowing them to retain data for future use. The files associated with the database, particularly those that carry the ".cblite" extension.
These databases lack integrated encryption capabilities, resulting in all stored information being in plain text.
If developers skip implementing the database’s full encryption API, local documents—including offline caches—remain fully exposed. The sensitive data in this application's database is, therefore, vulnerable to unauthorized access.
When an iOS app moves to the background, the operating system captures a screenshot of the current user interface to ensure a smooth return to the app.
While this feature enhances the user experience, it can raise security issues if sensitive information, such as financial details, personal data, or authentication screens, appears in the background image.
The cached screenshots are located within the file system at the specific path:
/Application/Containers/Data/Application/<AppID>/Library/SplashBoard/Snapshots/sceneID:*/
iOS captures app screenshots for fast app switching. These images may inadvertently include sensitive data like transaction details, OTPs, or chat messages.
Without mitigation, screenshots are stored unprotected and can be extracted from device backups or memory dumps.
NSLog is an essential function for iOS developers, widely used to debug iOS applications. It enables developers to check variable values, record essential notes, and pinpoint errors when a debugger isn't available.
While NSLog is useful during the development stage, it is crucial to disable it before launching the app in production. Keeping NSLog active in the production version could expose logged information to potential attackers who might access the physical device or other applications on a jailbroken device.
Log files can be generated through various methods. The subsequent list outlines the options available on iOS:
Verbose logging often leaks sensitive runtime data, including tokens, request payloads, headers, and errors.
NSLog output persists in device logs and can be accessed via Xcode or third-party tools. On jailbroken devices, attackers can systematically harvest this information.
iOS offers a seamless and sophisticated keyboard experience, incorporating functionalities such as auto-correction, predictive text, and keyboard caching.
A range of options, including autocorrect and spell check, is provided to users to streamline keyboard input. These options are, by default, cached in .dat files in the “/private/var/mobile/Library/Keyboard/” folder and its associated subdirectories.
The implementation of these features, including passwords, credit card details, or personal data.
The iOS keyboard learns and caches typed input to improve suggestions.
If not handled carefully, the implementation of keyboard caching may pose security vulnerabilities when managing sensitive information, including passwords, credit card numbers, or PII, which may be retained in the predictive text cache.
Attackers with device access can dump this cache and recover sensitive strings.
Storage mechanism |
Common mistake |
Real-world example |
Impact |
Secure practice |
NSUserDefaults |
Storing passwords or tokens in a plist |
Extractable via iTunes backup |
Account takeover |
Only store lightweight preferences |
Keychain |
Using AccessibleAlways |
Credentials are readable when locked |
Unauthorized access |
Use WhenUnlockedThisDeviceOnly |
SQLite / CoreData |
No encryption (default) |
PII extracted with forensic tools |
Identity theft, fraud |
Use SQLCipher or file-level encryption |
Realm |
Not enabling encryptionKey |
Plaintext Realm DB files |
Sensitive data exposure |
Configure encryption at initialization |
Couchbase Lite |
Default unencrypted files |
Offline caches fully exposed |
Data leaks & sync risks |
Enable Couchbase Lite’s encryption API |
Caching / Temp files |
Sensitive data in /tmp or screenshots |
Telegram cached deleted media |
Private data recovery |
Encrypt temp data, disable screen capture |
Logging (NSLog) |
Tokens or payloads logged |
Readable via Xcode or jailbreak tools |
Token hijack, replay attacks |
Disable verbose logging in production |
Keyboard Cache |
PII in predictive cache |
Passwords recovered from keyboard logs |
Credential leaks |
Disable autocorrect + secure text entry |
Abhinav Vasisth, Appknox’s Security Research Lead, believes that
“Developers often underestimate local storage risks on iOS, assuming the sandbox is enough. But attackers exploit weak configurations, not just OS flaws.”
Preventing insecure storage requires a defense-in-depth approach, combining secure APIs, strong encryption, and strict access controls.
Below are practical strategies, along with implementation snippets, that developers can apply immediately.
Store credentials and tokens only in Keychain. Configure accessibility with kSecAttrAccessibleWhenUnlocked or a stricter setting, never Always.
let account = "user@example.com"
let password = "SuperSecurePass!".data(using: .utf8)!
let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: account,
kSecValueData as String: password,
kSecAttrAccessible as String: kSecAttrAccessibleWhenUnlockedThisDeviceOnly
]
let status = SecItemAdd(query as CFDictionary, nil)
if status == errSecSuccess {
print("Securely stored in Keychain")
}
PRAGMA key = 'super_secret_key';
var config = Realm.Configuration()
let key = Data(count: 64)
_ = key.withUnsafeBytes { SecRandomCopyBytes(kSecRandomDefault, 64, $0.baseAddress!) }
config.encryptionKey = key
let realm = try! Realm(configuration: config)
let config = DatabaseConfiguration()
config.encryptionKey = EncryptionKey(password: "super_secret_key")
let database = try Database(name: "secure-db", config: config)
Avoid writing secrets to /tmp or /Library/Caches.
If required, encrypt before writing.
let sensitiveData = "PII-data".data(using: .utf8)!
let encrypted = try AES.GCM.seal(sensitiveData, using: key).combined
try encrypted?.write(to: secureURL)
self.view.window?.isSecure = true
This prevents the app’s view from being captured in the app switcher.
Remove sensitive data from logs. For production builds:
#if DEBUG
print("Debug info: \(debugData)")
#endif
textField.isSecureTextEntry = true
textField.autocorrectionType = .no
textField.spellCheckingType = .no
textField.smartInsertDeleteType = .no
Use CryptoKit instead of rolling custom crypto.
import CryptoKit
let key = SymmetricKey(size: .bits256)
let data = "Sensitive Info".data(using: .utf8)!
let sealedBox = try! AES.GCM.seal(data, using: key)
Local storage is often underestimated in iOS threat modeling.
Yet, forensic investigations consistently reveal apps leaking credentials, personal data, and tokens through insecure mechanisms.
Attackers no longer require jailbreaks or advanced exploits. Many insecure storage flaws can be exploited with basic tools.
Developers must treat local storage with the same rigor as network security: never assume the device is safe. Encryption, restricted APIs, and data minimization should be non-negotiable. Security teams must integrate static and dynamic analysis into the SDLC to catch unsafe storage practices before release.
In the current mobile threat landscape, secure storage is no longer optional; it has become a baseline expectation for any app handling user data.
Frequently Asked Questions
NSUserDefaults saves values in a plaintext .plist file inside the app’s container. Anyone with access to the device filesystem or backup can extract this data. It should only be used for non-sensitive preferences (e.g., UI settings, feature flags).
Use the iOS Keychain with strict accessibility attributes such as kSecAttrAccessibleWhenUnlockedThisDeviceOnly. This ensures data is only available when the device is unlocked and cannot be migrated to other devices.
Mark sensitive views as secure by setting self.view.window?.isSecure = true. This prevents iOS from capturing those views when the app transitions to the background.
The top mistakes developers make with iOS data storage security are: