Support

Lesson 10 of 11 ·11 min read

Mobile Security

The client is in the attacker's hands: what that means for your binary, your traffic, your storage, and your API.

After this lesson you can explain

  • Why anything shipped in the app binary is effectively published, and what that means for API keys and business logic.
  • Which of the three attackers (device owner, third parties, remote attackers) each mobile defence actually stops.
  • What certificate pinning and attestation buy, what they cost, and why authorization always lives server-side.

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:

AttackerTargetTypical 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, class-dump for structure). 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:

OK client-side, with restrictions

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). Unrestricted identifying keys are the ones that end up mined into someone else's bill.

Never in the binary

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  ──(vendor secret key)──►  Stripe / Claude / etc.
                                        │
                                        └── the only place secrets live;
                                            per-user rate limits + logging here

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, install the proxy's root certificate on the device, and read every request your app makes. HTTPS protects against third parties on the network; it does not protect against the device owner, who controls the trust store.

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 helpsPinning does not help
Against Attacker (b): hostile networks, rogue or compromised CAs, casual traffic snooping of your users Attacker (a): a determined device owner strips pinning with a Frida script in minutes

What it costs: operational risk. Pin the leaf certificate carelessly, rotate it, 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 the CA or intermediate rather than the leaf, ship backup pins, and keep a remotely-toggleable escape hatch. 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

TierWhatSafe for
Keychain / Keystore Hardware-backed secure storage; keys can be non-exportable Tokens, refresh tokens, encryption keys: the only 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 World-readable areas Nothing sensitive, ever

Then the leak channels people forget, the source of most real-world mobile data leaks, well ahead of broken cryptography:

  • Logs: a token printed to logcat rides along in bug reports and crash uploads. Strip logging of secrets in release builds.
  • 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 / DeviceCheck (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 app obtains a signed verdict from the OS; your server validates it with Google/Apple before honouring sensitive requests.

  • 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

DoAgainstCost
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

SymptomCause
A shocking vendor bill from usage you never generatedSecret key shipped in the binary and scraped by bots
Users accessing each other's data by changing an IDEndpoint trusted the app to only send the user's own IDs (§4)
Cracked builds with premium unlocked circulatingEntitlement checked client-side only (payments lesson, same row)
The app stops working for everyone after a certificate renewalLeaf-certificate pinning without backup pins (§5)
Tokens showing up in crash reportsDebug logging left on in release builds
Sensitive screen visible in the phone's app switcherNo snapshot blanking on backgrounding
Fraud bots surviving every client-side check you addChecks run on hardware the attacker controls; move the decision server-side, add attestation as cost
Legitimate users on rooted phones locked outAttestation used as a hard gate where a risk signal was appropriate

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?

Reviewed 4 August 2026.