A mobile app runs on a device you do not control. Anyone who downloads it can unpack it, inspect its files and watch what it does. That changes how you should think about secrets.
If a secret is inside the app package, or written to the device in a readable form, assume someone will find it. The question is not whether it can be extracted, but what an attacker can do once they have it.
Hardcoded keys in the app
The most common problem is a secret compiled into the app: an API key, a cloud storage credential, a third-party service token or a signing key.
App packages are easy to unpack. An Android APK is a zip file, and iOS apps can be decrypted and examined on a test device. Strings tools, decompilers and simple text searches will surface most embedded keys. Obfuscation slows this down but does not prevent it.
Ask: what can this key do? A public, restricted key for maps or analytics may be acceptable. A key that can read your database, send email as you or access a cloud bucket is not.
Fix: never ship keys that grant meaningful access. Have the app call your own backend, authenticate the user there, and let the backend hold the real credentials.
Readable local storage
Apps often save data locally for speed or offline use. The risk depends on where it is saved.
On Android
- SharedPreferences are plain XML files. Fine for settings, not for tokens or personal data.
- Local databases and files are readable by anyone with access to the device’s app data, for example on a rooted device or through a backup.
- External storage can be readable by other apps.
On iOS
- NSUserDefaults is a plain property list. Fine for preferences, not for secrets.
- App files and caches may hold API responses, images or documents containing personal data.
Both platforms provide a proper place for secrets: the Keychain on iOS and the Android Keystore (often used through EncryptedSharedPreferences). These use hardware-backed protection where available and can tie access to the device being unlocked.
Backups and other side channels
Even well-designed storage can leak through routes the developer did not consider:
- Device backups may include app data unless you exclude sensitive files.
- Logs printed during development sometimes stay in release builds and capture tokens or responses.
- Screenshots in the app switcher can show sensitive screens.
- Keyboard caches and clipboards can retain what users type or copy.
- WebView caches can store pages and cookies from in-app browsing.
Each of these is minor on its own. Together, they often give an attacker with brief access to a device more than expected.
Reverse engineering and tampering
Attackers do not stop at reading files. They can modify the app, bypass checks that only run on the device, and replay or alter its traffic.
That is why any security decision the app makes on its own, such as “is this user premium?” or “is this device trusted?”, must also be enforced by the server. Jailbreak and root detection, certificate pinning and anti-tampering controls raise the effort for an attacker. They are not a replacement for server-side checks.
A practical checklist
- Search your own builds for keys, URLs and credentials before release.
- Move every privileged credential to the backend.
- Use the Keychain or Keystore for tokens; use plain storage only for non-sensitive settings.
- Exclude sensitive files from backups, and remove debug logging from release builds.
- Clear local data on logout, including caches and WebView storage.
- Enforce access control on the server, never only in the app.
These areas map to the storage, cryptography and resilience controls in the OWASP Mobile Application Security Verification Standard (MASVS), which our mobile app penetration testing follows for both iOS and Android.
Not sure what your app leaves on the device? Ask us to take a look.
