Mobile app security
A mobile binary runs on hardware the attacker owns. They can decompile it,
read its storage, and proxy its traffic on a rooted phone. The recurring
mistakes all treat the device as trusted: secrets dropped in plain
preferences, a binary that carries its own API keys, and obfuscation mistaken
for a control. Build for an environment where the user may be the adversary.
Method
- Store secrets in the hardware-backed store, not flat files. Put tokens
and keys in the iOS Keychain with
kSecAttrAccessibleWhenUnlockedThisDeviceOnly, or the Android Keystore,
never in UserDefaults, SharedPreferences, SQLite, or a plist. Those
sit in cleartext in the sandbox and in cloud backups.
- Ship no long-lived secret in the binary. Anything compiled in falls out
to
strings, Hopper, or jadx. Keep API secrets server-side, hand the app
short-lived tokens, and route any third-party call that needs a secret
through your own backend.
- Pin certificates only where you can rotate fast. Pin to the CA or
intermediate public key rather than the leaf, and ship at least one backup
pin so a cert renewal does not brick installed apps. Skip pinning when you
cannot push an update quickly enough to survive an emergency key change.
- State the ceiling on obfuscation. R8, ProGuard, or a commercial packer
buys minutes to hours against a reverse engineer, not permanence. Use it to
slow analysis, never as the barrier between an attacker and a value that
must stay secret, since Frida reads it at runtime regardless.
- Keep sensitive data off the OS surface. Exclude token files from iCloud
and Android auto-backup, set
FLAG_SECURE or blur on backgrounding so
screens stay out of the app switcher and screenshots, and validate deep
links and custom URL schemes as untrusted input.
- Treat root and jailbreak detection as signal, not a gate. It helps fraud
scoring and can trip a casual attacker, but any client check runs on the
attacker's device and gets patched out. Enforce every trust decision on the
server.
Signals
- Does pulling the app sandbox off a rooted device reveal any token in
cleartext?
- Does a server cert renewal keep old installs working through a backup pin?
- Can the app still enforce authorization once root detection is bypassed?
- Do sensitive screens stay blank in the task switcher and screenshots?
Boundaries
This addresses on-device storage, transport trust, and binary hardening. It
does not cover the API the app talks to (see api-security) or the flow that
issues its tokens (see oauth-flows). Server-side enforcement stays the real
control: the client hardens, it does not decide.
1---2name: mobile-app-security3description: Protect a mobile app by storing secrets in the platform keystore, pinning certificates only where rotation is controlled, and treating obfuscation as delay rather than defense. Use when building or reviewing an iOS or Android app that holds tokens, keys, or user data on device.4---56# Mobile app security78A mobile binary runs on hardware the attacker owns. They can decompile it,9read its storage, and proxy its traffic on a rooted phone. The recurring10mistakes all treat the device as trusted: secrets dropped in plain11preferences, a binary that carries its own API keys, and obfuscation mistaken12for a control. Build for an environment where the user may be the adversary.1314## Method15161. **Store secrets in the hardware-backed store, not flat files.** Put tokens17 and keys in the iOS Keychain with18 `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`, or the Android Keystore,19 never in `UserDefaults`, `SharedPreferences`, SQLite, or a plist. Those20 sit in cleartext in the sandbox and in cloud backups.212. **Ship no long-lived secret in the binary.** Anything compiled in falls out22 to `strings`, Hopper, or jadx. Keep API secrets server-side, hand the app23 short-lived tokens, and route any third-party call that needs a secret24 through your own backend.253. **Pin certificates only where you can rotate fast.** Pin to the CA or26 intermediate public key rather than the leaf, and ship at least one backup27 pin so a cert renewal does not brick installed apps. Skip pinning when you28 cannot push an update quickly enough to survive an emergency key change.294. **State the ceiling on obfuscation.** R8, ProGuard, or a commercial packer30 buys minutes to hours against a reverse engineer, not permanence. Use it to31 slow analysis, never as the barrier between an attacker and a value that32 must stay secret, since Frida reads it at runtime regardless.335. **Keep sensitive data off the OS surface.** Exclude token files from iCloud34 and Android auto-backup, set `FLAG_SECURE` or blur on backgrounding so35 screens stay out of the app switcher and screenshots, and validate deep36 links and custom URL schemes as untrusted input.376. **Treat root and jailbreak detection as signal, not a gate.** It helps fraud38 scoring and can trip a casual attacker, but any client check runs on the39 attacker's device and gets patched out. Enforce every trust decision on the40 server.4142## Signals4344- Does pulling the app sandbox off a rooted device reveal any token in45 cleartext?46- Does a server cert renewal keep old installs working through a backup pin?47- Can the app still enforce authorization once root detection is bypassed?48- Do sensitive screens stay blank in the task switcher and screenshots?4950## Boundaries5152This addresses on-device storage, transport trust, and binary hardening. It53does not cover the API the app talks to (see api-security) or the flow that54issues its tokens (see oauth-flows). Server-side enforcement stays the real55control: the client hardens, it does not decide.