Mobile Security
The client is in the attacker's hands: what that means for your binary, your traffic, your storage, and your API.
1. The threat model: three different attackers
Every mobile defence protects against a specific attacker, and muddling them is how teams end up defending the wrong thing. Keep three separate:
| Attacker | Target | Typical moves |
|---|---|---|
| (a) The device owner | Your business | Cracking premium features, extracting keys, scripting your API, cheating |
| (b) Third parties | Your users | Malware, stolen phones, hostile WiFi, phishing |
| (c) Remote attackers | Your backend | Abusing whatever the app reveals: keys, endpoints, weak authorization |
The recurring confusion: defences against (b), like certificate pinning, get deployed as if they stopped (a). They do not, because the device owner controls the device. The two rules below make that concrete.
2. Rule one: the binary is public
An APK is literally a zip file. Free tools recover something close to source in minutes:
- jadx / apktool decompile Android bytecode into readable Java/Kotlin-shaped code.
- One command dumps every string in the binary: keys, URLs, feature flags, snarky comments.
- iOS is more locked down but not meaningfully safer against a motivated analyst (Frida for runtime hooking). Assume symmetry.
Obfuscation (R8/ProGuard on Android) renames checkEntitlement() to a.b() and raises the effort floor. Enable it (it is free and also shrinks the binary), but understand what it buys: it delays reading, it does not prevent it.
Therefore: anything you ship in the app, you have published. API keys, "hidden" endpoints, algorithms, the admin-mode flag, the discount logic: all of it is public the moment the app is downloadable. Decisions that must stay secret or must be enforced belong on the server, full stop.
3. API keys in apps: what they really expose
Keys split into two kinds, and the split decides everything:
Identifying keys
A Google Maps key, a Firebase config, an analytics write key: they say which project is calling and are designed to be embedded, provided you use the platform's restrictions to bind them to your app's package name and signing certificate (and/or per-API quotas). For Firebase specifically, the real protection is Security Rules, not config secrecy. Unrestricted identifying keys are the ones that end up mined into someone else's bill.
Secret keys
A Stripe secret key, a Claude or OpenAI API key, service-account credentials, a database password: these grant spending or data authority to whoever holds them. Shipping one in an app is publishing it (§2). The scraped-key pipeline is automated: bots trawl app binaries and public repos continuously, and the time from leak to abuse is hours, not weeks.
The correct architecture is the one this course has been building all along:
App │ user auth: bearer token ▼ YOUR backend │ ── the only place secrets live; │ per-user rate limits + logging ▼ vendor secret key Stripe / Claude / etc.
The app authenticates users to your backend; your backend holds the vendor secrets and proxies the calls, which also gives you per-user rate limiting, usage attribution, and one place to rotate a leaked key.
4. Rule two: your API is public too
Anyone can put an intercepting proxy (mitmproxy, Charles, Burp) between their own phone and the internet and read every request your app makes. On modern Android it takes more than one settings toggle (release builds have not trusted user-installed certificates since Android 7), but a rooted device, a repackaged build, or a Frida script gets there routinely, and all three are everyday tools for the device owner. HTTPS protects against third parties on the network; it does not protect against the device owner, who controls the client.
So the moment your app ships, your entire API surface is documented for anyone curious: every endpoint, parameter, header, and response shape. Consequences:
- There are no hidden endpoints. "The app never calls it with other IDs" is not a security property.
- Security-by-obscurity is void. Assume attackers replay, reorder, and mutate every call your app can make.
- Authorization is enforced server-side on every request. Does this token's user get to do this action on this resource? Checked every time, never inferred from "the app wouldn't send that."
Fourth appearance of the course's rule. Login: verify the ID token server-side. Sync: the server owns truth. Payments: verify the receipt server-side. Security: authorize every call server-side. The client is a view of what the user may do; it is never the enforcement of it.
5. Certificate pinning: what it buys and what it costs
What it is: the app refuses TLS unless the server presents your specific certificate or public key, rather than anything the device's trust store would accept.
| Pinning helps | Pinning does not help | |
|---|---|---|
| Against | Attacker (b), beyond what plain TLS already covers: a rogue or compromised CA, a mis-issued certificate, a user-installed trust anchor | Attacker (a): a determined device owner strips pinning with a Frida script in minutes |
What it costs: operational risk. Pin carelessly, rotate the certificate, and every installed copy of your app bricks its own connectivity until users update: a self-inflicted outage with a store-review delay on the fix. If you pin: pin public keys (SPKI hashes) through the platform's supported configuration rather than raw certificates, ship at least one backup pin for a key you control, and test the rotation path before you need it. Banks and messengers pin; a content app reasonably may not. It is a trade-off, not a default.
6. On-device storage: a hierarchy, not a binary
| Tier | What | Safe for |
|---|---|---|
| Keychain / Keystore | The platform secret stores. iOS Keychain holds small secrets in an encrypted database; Android's Keystore manages non-exportable keys that encrypt secrets kept in app-private storage (hardware-backed where the device supports it) | Tokens, refresh tokens, encryption keys: the correct home for credentials |
| App-private storage | Sandboxed files/DB, OS-encrypted at rest | Cached content, preferences (on a non-rooted device). Assume readable on a rooted/jailbroken one |
| External / shared storage | Areas outside the sandbox; can be user-visible, exported, or swept into backups | Nothing sensitive, ever |
Secure storage limits extraction; it does not make a stolen token harmless. The other half of the defence is lifecycle: short-lived access tokens, rotated refresh tokens, server-side revocation (the design the login lesson built), and clearing credentials on logout.
Then the leak channels people forget, the source of far more real-world mobile leaks than broken cryptography:
- Logs: a token printed to logcat rides along in bug reports and crash uploads. Keep secrets out of logging in every build; debug builds end up in support bundles too.
- Analytics events quietly carrying emails, tokens, or content into a third-party dashboard.
- Backups: device backups hoover up app data unless you exclude sensitive files.
- The app-switcher screenshot: the OS snapshots your UI on backgrounding; banking apps blank the screen first for exactly this reason.
- The clipboard: readable by other apps on older Android versions; don't leave secrets on it.
The cheapest storage defence is upstream: data never stored cannot leak. Cache aggressively for UX (the sync lesson), but keep genuinely sensitive material server-side, fetched on demand over the authenticated API; that is also exactly the notify-then-fetch reasoning from the push lesson.
7. Attestation: the modern arms race
Play Integrity (Android) and App Attest (iOS) let your backend ask the platform a question no app-side check can answer honestly: "is this request coming from my unmodified app, installed from the store, on a genuine device?" The flow matters: your server issues a challenge, the app obtains a signed verdict bound to it, and your server validates the signature and rejects replays before honouring the request; an unbound verdict can be harvested and reused. (iOS also has DeviceCheck: two server-managed bits of per-device fraud state, a useful signal but not attestation.)
- Use it for: raising the cost of bots, credential-stuffing, fake-purchase fraud, and modified clients on your high-value endpoints (login, purchase, promo redemption).
- Don't mistake it for a wall: it is an arms race with active bypass communities, and it can false-positive on legitimate rooted or de-Googled devices; decide deliberately whether those users matter to you.
- Client-side root/jailbreak detection alone is the weak version of this: it runs on hardware the attacker controls, so treat it as a signal, never a gate for anything valuable.
8. The 80/20 checklist
| Do | Against | Cost |
|---|---|---|
| Server-side authorization on every endpoint | (a), (c) | Discipline, not code volume; the single highest-value item |
| No secret keys in the binary; proxy vendor calls through your backend | (a), (c) | One extra endpoint per vendor |
| Verify purchases/receipts server-side (payments lesson §2) | (a) | Already required for correctness |
| Tokens in Keychain/Keystore only | (b) | A library call |
| Nothing sensitive in logs, analytics, or backups | (b) | Release-build config + review habit |
| TLS everywhere; pin only with a rotation plan | (b) | Free / operational risk respectively |
| Obfuscation (R8/ProGuard) on | (a) | Free; raises the floor |
| Rate limits + anomaly monitoring on the API | (a), (c) | Server-side; catches what everything above misses |
| Attestation on high-value endpoints, as a cost-raiser | (a), (c) | Moderate; mind false positives |
9. Common gotchas
| Symptom | Cause |
|---|---|
| A shocking vendor bill from usage you never generated | Secret key shipped in the binary and scraped by bots |
| Users accessing each other's data by changing an ID | Endpoint trusted the app to only send the user's own IDs (§4) |
| Cracked builds with premium unlocked circulating | Entitlement checked client-side only (payments lesson, same row) |
| The app stops working for everyone after a certificate renewal | Leaf-certificate pinning without backup pins (§5) |
| Tokens showing up in crash reports | Debug logging left on in release builds |
| Sensitive screen visible in the phone's app switcher | No snapshot blanking on backgrounding |
| Fraud bots surviving every client-side check you add | Checks run on hardware the attacker controls; move the decision server-side, add attestation as cost |
| Legitimate users on rooted phones locked out | Attestation used as a hard gate where a risk signal was appropriate |
Deliberately out of scope here: this lesson covers the API half of mobile security, because that is where an API developer's leverage is. The platform half (permission minimisation, deep links and intents, exported Android components, WebView hardening, dependency and build-chain security) is its own discipline; OWASP MAS in the sources below is the reference to work through when an app grows real surface area.
10. The arc so far
Ten lessons, one arc. The app is an API client (lesson 2). Its login is token verification (3). Its notifications are two API integrations joined at a token (4). Its offline story is a contract of deltas and idempotency (7). Its payments are receipts verified server-side (8). Its real-time layer is a delta feed over the same source of truth (9). And its security model reduces to the same sentence every one of those lessons kept arriving at:
The client reports; the server verifies and decides. The binary is public, the traffic is public, the device belongs to someone else. The server is the only ground you control, which means mobile security is mostly API security, and an API developer learning mobile is not entering foreign territory. They are meeting their own subject from the other side.
One lesson remains: the case studies, which test this whole mental model against real apps.
Check your understanding
- Which attacker does certificate pinning actually defend against, and which one can strip it in minutes?
- A Stripe secret key and a Google Maps key both sit in your codebase: which one may ship in the binary, and under what condition?
- Why is client-side root detection a signal rather than a gate, and what does attestation change about that?
Sources and further reading
- OWASP Mobile Application Security
- Apple Platform Security
- Android security documentation
- Android Network Security Configuration (the user-CA trust behaviour in §4)
Reviewed 7 August 2026.