Wireless & Mobile Security · beginner · ~11 min

Mobile app security model (Android & iOS)

- 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

Overview

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).

Why it matters

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.

Core concepts

1. The app sandbox

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.

2. Permissions (runtime, least privilege)

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.

3. Code signing

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.

4. Where findings concentrate

   [ 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

5. Android vs iOS (at a glance)

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:

  1. Given the sandbox is strong, name the four places findings usually concentrate.
  2. Does a valid code signature mean an app is safe? Explain.
  3. Why is "requests more permissions than it needs" a finding?

Syntax notes

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.

Lesson

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.

The model

  • App sandbox: each app runs as its own user with a private data directory that other apps cannot read. The OS controls access to shared resources.
  • Permissions: access to the camera, location, contacts, and storage is granted one permission at a time, ideally at runtime. Requesting more permissions than the app needs is a finding.
  • Code signing: apps are signed. iOS only runs App-Store or enterprise-signed code. Android verifies signatures when an app is updated.

Android vs iOS

Android

  • Apps ship as APK (or AAB) packages.
  • An APK is essentially a zip containing compiled DEX bytecode, resources, and a manifest (a file that declares the app's components and permissions).
  • The platform is more open, with sideloading and a larger malware surface.

iOS

  • Apps ship as IPA packages of compiled native code.
  • The ecosystem is more closed. Deep inspection usually requires a jailbreak (removing the device's built-in restrictions).

Where the bugs are

Because the sandbox is relatively strong, mobile findings concentrate in a few areas:

  • insecure local storage of secrets
  • weak or missing transport security
  • weak authentication
  • exposed app components (on Android, exported activities and content providers)
  • client-side trust — secrets or logic placed in the app itself

These map to the OWASP Mobile Top 10 / MASTG, covered in the next lesson.

Testing posture

Mobile testing combines two approaches:

  • Static analysis: decompile the app and read its code.
  • Dynamic analysis: observe storage, logs, and API traffic while the app runs.

Both are covered next. Test only apps you are authorized to assess.

Code examples

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.

Line by line

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.

Common mistakes

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.

Debugging tips

  • Where's the app storing data? Inspect the app-private directory on a test device/emulator you own; plaintext secrets there are a finding.
  • Is traffic actually encrypted? Proxy the app on your authorized test device; cleartext or a failure to validate certs is the issue.
  • Which components are reachable? Read the manifest for exported="true"; question each one.
  • Can't inspect an iOS app? Static review of the IPA + traffic analysis often suffices without a jailbreak.

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?

Memory safety

Security & safety — defensive posture.

  • Treat the client as untrusted: anything shipped in the app can be read by whoever installs it. Keep secrets server-side.
  • Use the platform keystore for anything sensitive on-device; never plaintext files or shared storage.
  • Log security-relevant events on the backend (auth, failures) — not on the device, and never log tokens or PII.
  • False assumption to avoid: "it's signed / it's in the store, so it's safe" — signing is authenticity, not safety.
  • All inspection happens on devices/emulators you own or are authorized to test, with test accounts only.

Real-world uses

  • App-store review + code signing provide baseline integrity, but security teams still test apps for storage/traffic/permission issues.
  • Mobile banking/health apps rely on keystore-backed secrets, TLS with pinning, and minimal permissions.
  • MDM/enterprise enforces device policy, but the app must still be built securely.

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.

Practice tasks

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.

Summary

  • Mobile apps run in a strong sandbox with runtime permissions, so findings concentrate in storage, traffic, exported components, and the backend — not sandbox escapes.
  • Code signing proves authenticity, not safety.
  • Keep secrets on the backend, use the keystore on-device, force TLS, and turn off debuggable/allowBackup.
  • Apply least privilege to permissions.
  • Treat the client as untrusted; test only apps/devices you own.

Next: Mobile testing — using the OWASP MASTG to inspect storage and traffic in an authorized lab.