Wireless & Mobile Security · beginner · ~11 min
- Explain the mobile **app sandbox**, per-app isolation, and the runtime **permission** model - Describe **code signing** on Android and iOS and what it does (and doesn't) guarantee - Identify where mobile findings actually concentrate: **local storage, network traffic, exported components, and the backend** - Compare Android and iOS security models at a useful level - Apply **least privilege** and secure defaults to a mobile app
Mobile platforms run each app inside a sandbox with a permission model — stronger default isolation than a desktop. That single fact reshapes where bugs live: because one app usually can't read another's data, most real findings are about how an app handles its own data, its network traffic, and its backend, not about escaping the sandbox.
This is a foundation lesson (no prerequisites). It sets up Mobile testing, where you'll actually inspect an app. Understanding the model first tells you what to look for.
The defensive theme: the OS gives you strong isolation for free; the app's job is to not undermine it (over-broad permissions, secrets in storage, cleartext traffic).
Billions of people run sensitive workflows — banking, health, messaging — on mobile apps. The platform sandbox raises the floor, but apps routinely lower it: storing tokens in world-readable places, shipping API keys in the package, allowing cleartext traffic, or exporting components that shouldn't be. Knowing the model lets you find and fix those without chasing exotic sandbox escapes.
Definition. Each installed app runs as its own user with a private data directory other apps can't read. The OS mediates access to shared resources (files, camera, network).
Implication: an attacker rarely reads another app's data directly. Findings cluster around what the app exposes — its own storage, its traffic, and any components it makes reachable.
Access to camera, location, contacts, microphone, and storage is granted one permission at a time, ideally at runtime with user consent.
Finding: an app requesting more permissions than its function needs is a privacy/attack-surface problem. Least privilege applies: request only what you use, when you use it.
Apps are signed. iOS runs only App-Store or enterprise-signed code; Android verifies the signature on update (an update must be signed by the same key).
What it guarantees: integrity and a consistent publisher — not that the code is safe. Signed malware is still malware; signing is authenticity, not trustworthiness.
[ App sandbox: strong ] <-- rarely the bug
| local storage --> secrets/tokens stored insecurely
| network traffic --> cleartext / no cert validation
| IPC / exported --> components reachable by other apps
v backend API --> the same web/API bugs you already know
| Aspect | Android | iOS |
|---|---|---|
| Distribution | Play + sideloading (APK) | App Store (IPA), tighter |
| Components | Activities/Services/Providers, some exported | Fewer cross-app entry points |
| Storage | App-private dir; misuse = world-readable files | Keychain for secrets; misuse = plist/files |
| Inspection | Easier (APK = zip, decompile DEX) | Harder without jailbreak |
Knowledge check:
Two config surfaces you'll review most (Android shown; iOS has analogous plist keys):
<!-- AndroidManifest.xml — the app's security-relevant configuration -->
<application
android:debuggable="false" <!-- true in a shipped app = finding -->
android:allowBackup="false" <!-- true can leak app data via backup -->
android:usesCleartextTraffic="false"> <!-- true allows unencrypted HTTP -->
<activity android:name=".Public" android:exported="false"/> <!-- expose only if needed -->
</application>
Reviewing these flags is often the fastest way to find real issues.
Mobile platforms run apps inside a sandbox with a permission model. This is a stronger default isolation than desktop, and it shapes where the bugs are.
Android
iOS
Because the sandbox is relatively strong, mobile findings concentrate in a few areas:
These map to the OWASP Mobile Top 10 / MASTG, covered in the next lesson.
Mobile testing combines two approaches:
Both are covered next. Test only apps you are authorized to assess.
An insecure → secure comparison of the two things most often wrong in a mobile app: where secrets live and how it talks to its backend.
WARNING: Intentionally vulnerable training example — for a local, authorized test app only. Do not ship.
INSECURE
- Auth token saved to a plain file in shared/world-readable storage
- AndroidManifest: usesCleartextTraffic="true", debuggable="true"
- API key hardcoded in the app package
SECURE (the fix)
- Store tokens in the platform keystore (Android Keystore / iOS Keychain),
never in plain files or shared storage
- usesCleartextTraffic="false"; use HTTPS/TLS with a Network Security Config
- No secrets in the client: the app calls YOUR backend, which holds the key
- debuggable="false", allowBackup="false"; export only components that must be
VERIFY
- Inspect app-private storage: no plaintext token present
- Capture traffic on your test device: it's TLS, cleartext attempts fail
- Grep the package: no API key / secret strings
Why the fix works. Secrets in a client can always be extracted, so they belong on the backend; the OS keystore protects what must stay on-device; disabling cleartext + validating TLS stops network interception; and turning off debuggable/backup removes easy data-extraction paths. The VERIFY steps are how a tester confirms each fix in a lab.
Reading the secure column as a checklist:
| Control | Threat it removes | How to confirm |
|---|---|---|
| Keystore/Keychain for tokens | Token theft from storage | No plaintext token in app data |
usesCleartextTraffic=false + TLS |
Traffic interception | Only TLS on the wire; HTTP blocked |
| No client-side secrets | API-key extraction | Package grep finds no key |
debuggable=false, allowBackup=false |
Easy data extraction | Flags off in the manifest |
exported only where needed |
Other apps invoking components | Non-exported unless required |
The pattern mirrors the "findings concentrate" diagram: fix storage, traffic, exposure, and backend trust.
Mistake 1 — secrets in the client. Wrong: hardcoding an API key in the app "because it's compiled." Why wrong: packages are trivially unpacked; the key is extractable. Fix: keep secrets on your backend; the app authenticates to it.
Mistake 2 — cleartext traffic.
Wrong: usesCleartextTraffic="true" / plain HTTP to the API.
Why wrong: anyone on the network path can read/modify it.
Fix: HTTPS everywhere; disable cleartext; validate certificates.
Mistake 3 — over-broad permissions. Wrong: requesting contacts/location "just in case." Why wrong: expands attack surface and harms privacy. Fix: least privilege — request only what a feature needs, when it runs.
Recognize it: if the manifest has debuggable="true" or a hardcoded key, you've found the bug.
exported="true"; question each one.Questions to ask: Are any secrets in the package or storage? Is all traffic TLS with cert validation? Which components are exported, and do they need to be? Which permissions are unused?
Security & safety — defensive posture.
Beginner habits: no secrets in the client; HTTPS only; least-privilege permissions; debuggable=false.
Advanced habits: certificate pinning with a safe update path; keystore-backed crypto; threat-model the backend as the real trust boundary.
Beginner 1 — Manifest audit. Objective: given a manifest with debuggable="true", usesCleartextTraffic="true", and an exported activity, list each finding and its fix. Concepts: secure defaults.
Beginner 2 — Where's the secret? Objective: explain why a hardcoded API key in an app is extractable and where it should live instead. Concepts: client-is-untrusted.
Intermediate 1 — Storage plan. Objective: for a token, a user setting, and a cached image, say where each should be stored on Android/iOS and why. Concepts: keystore vs app storage.
Intermediate 2 — Permission diet. Objective: given an app that requests camera, location, contacts, and storage but only scans QR codes, decide which permissions to drop and justify. Concepts: least privilege.
Challenge — Secure-by-default checklist. Objective: produce a one-page "secure defaults" checklist for a new mobile app (storage, traffic, permissions, components, backend trust), each with a one-line rationale and how you'd verify it. Requirements: defensive; test only your own app. Concepts: everything in this lesson; sets up Mobile testing.
debuggable/allowBackup.Next: Mobile testing — using the OWASP MASTG to inspect storage and traffic in an authorized lab.