---
name: harden-mobile-apps
description: "Static and runtime hardening for Android and iOS apps running on devices an attacker controls: APK/IPA reverse engineering, rooted or jailbroken devices, Frida or Xposed hooking, repackaging, certificate pinning bypass, and secrets read from local storage (MITRE ATT&CK Mobile T1617, T1540, T1626, T1655, T1638, T1634, T1533). Signals come mostly from server-side attestation: Google Play Integrity verdicts, Apple App Attest and DeviceCheck, Android Key Attestation, Firebase App Check, RASP tamper events, and MDM/UEM compliance. Gives hardening controls (TLS pinning, Keystore/Keychain storage, obfuscation, RASP, attestation enforcement), detection, a hunt, and OWASP MASVS mapping. Use when hardening or reviewing an Android or iOS app, pinning TLS or storing secrets on a mobile client, choosing between Play Integrity, App Attest, and App Check, triaging root/jailbreak or Frida alerts, or hunting repackaged clients hitting your API."
---

# Mobile App Hardening (Android / iOS) - Defensive Methodology

> **Tier 2 - methodology skill.** No bespoke UMBRASEC detection rule ships with this skill yet. It gives the defender's method, the log sources and signals, and the hardening, and points to public rule and reference sources. Promotion to Tier 1 (a sourced, tuned UMBRASEC rule) is tracked in [COVERAGE.md](https://github.com/umbrasec-labs/umbra-blue/blob/main/COVERAGE.md). Do not treat the content below as citing a UMBRASEC rule; there is none for this threat yet.

## TL;DR

A mobile app runs on hardware the attacker owns, so every client-side check can eventually be patched out. The only durable control is to **treat the client as untrusted and enforce trust at the backend through attestation**: have the server require and verify a fresh **Google Play Integrity** token (Android) or **Apple App Attest** assertion (iOS), reject requests that fail, and never let a client-side "device is clean" boolean be the gate. Layer four client-side hardening measures on top as defense in depth, not as the boundary: **TLS certificate or public-key pinning** so a stripped-CA proxy cannot read traffic, **secure storage** in the Android Keystore (StrongBox where present) and iOS Keychain with Secure Enclave rather than shared preferences or plists, **code shrinking and obfuscation** (R8/ProGuard, and a commercial obfuscator for high-value logic) to raise reverse-engineering cost, and **RASP** (root/jailbreak, debugger, emulator, and Frida/Xposed detection) that reports tamper telemetry to the server. Map the work against **OWASP MASVS v2** and test it with the **OWASP MASTG**. This skill is defensive and ships no reverse-engineering, hooking, pinning-bypass, repackaging, or rooting tooling.

## What it looks like, defender view

You rarely see the attacker on the device; you see the consequences at your backend and in attestation telemetry. The tells, in rough order of signal strength:

- **Play Integrity `appIntegrity.appRecognitionVerdict` of `UNRECOGNIZED_VERSION`** while `packageName` matches yours. The app that produced the token is not a Play-distributed build of your app: a repackaged, re-signed, or patched client. Pair with `certificateSha256Digest` not matching your signing certificate.
- **`deviceIntegrity.deviceRecognitionVerdict` missing `MEETS_DEVICE_INTEGRITY`** (only `MEETS_BASIC_INTEGRITY`, only `MEETS_VIRTUAL_INTEGRITY`, or empty). The request came from a rooted device, an emulator, or a device that failed boot integrity. On iOS the equivalent is an **App Attest assertion that fails server verification** (bad nonce, wrong rpId hash, non-incrementing counter, or a cert chain that does not root to Apple's App Attest CA).
- **`environmentDetails.appAccessRiskVerdict.appsDetected` of `KNOWN_CAPTURING`, `KNOWN_OVERLAYS`, or `KNOWN_CONTROLLING`**, and **`playProtectVerdict` of `HIGH_RISK` / `MEDIUM_RISK`**: screen-capture, overlay, or remote-control tooling active during the session.
- **A burst of TLS handshake failures or pin-validation errors** reported by RASP, or a sudden population of sessions whose traffic traverses a user-added CA. This is pinning-bypass probing or an intercepting proxy.
- **RASP tamper events**: Frida gadget or server detected, `ptrace`/debugger attached (`TracerPid` nonzero in `/proc/self/status`), Magisk/Zygisk or Xposed present, jailbreak artifacts on iOS, emulator fingerprint. One device producing many of these in a short window is an analyst or attacker working the app, not a normal user.

## Log sources and signals

The authoritative signal is server-side attestation verification, because it cannot be forged by patching the client. Collect and alert on:

- **Google Play Integrity API** (the successor to the now-retired SafetyNet Attestation). Your backend decrypts and verifies the integrity token, then logs the verdict fields:
  - `appIntegrity.appRecognitionVerdict` (`PLAY_RECOGNIZED`, `UNRECOGNIZED_VERSION`, `UNEVALUATED`), `packageName`, `versionCode`, `certificateSha256Digest`.
  - `deviceIntegrity.deviceRecognitionVerdict` (`MEETS_DEVICE_INTEGRITY`, `MEETS_STRONG_INTEGRITY`, `MEETS_BASIC_INTEGRITY`, `MEETS_VIRTUAL_INTEGRITY`).
  - `accountDetails.appLicensingVerdict` (`LICENSED` / `UNLICENSED`), `environmentDetails.appAccessRiskVerdict.appsDetected`, `environmentDetails.playProtectVerdict`.
  - `requestDetails.requestHash` and `timestampMillis` to bind the verdict to a specific request and detect replay.
- **Apple App Attest (`DCAppAttestService`) and DeviceCheck.** The server verifies the attestation object (CBOR with an x5c chain) against the **Apple App Attest Root CA**, checking the nonce, the `rpId` (App ID) hash, the counter, and the credential ID, then verifies each subsequent assertion's signature and monotonically increasing counter. DeviceCheck gives two per-device bits (with a last-updated timestamp); App Attest receipts can additionally be exchanged for a fraud risk metric. Log verification pass/fail with the reason.
- **Android hardware Key Attestation.** A key generated in the Keystore can produce an attestation certificate chain rooted in the Google hardware attestation root, carrying `verifiedBootState` and `deviceLocked`. Use it to assert hardware-backed key storage and a locked bootloader.
- **Firebase App Check.** Metrics and enforcement state per API, backed by Play Integrity (Android) and App Attest (iOS); the console shows verified vs unverified request ratios per service.
- **RASP / in-app protection telemetry.** Tamper events the app reports to your backend: root/jailbreak detection, Frida (frida-server on the default port, `re.frida.server`, gadget libraries, named pipes), Xposed/LSPosed, Magisk/Zygisk, debugger (`android.os.Debug.isDebuggerConnected`, nonzero `TracerPid`), emulator fingerprints, and hook detection. On iOS: jailbreak artifacts (Cydia/Sileo, writable paths outside the sandbox, suspicious dynamic libraries via the dyld image list, `DYLD_INSERT_LIBRARIES`).
- **MDM / UEM compliance.** Microsoft Intune device compliance state and Jamf/other UEM posture, where the app is enterprise-managed, as a corroborating signal.
- **Backend / API gateway and WAF logs.** Correlate the attestation verdict with the API request: version mismatch, missing/expired integrity token, pin-failure reports, and anomalous per-account or per-device request rates.

**Baseline.** In a healthy population the overwhelming majority of sessions present `PLAY_RECOGNIZED` + `MEETS_DEVICE_INTEGRITY` (or a valid App Attest assertion), a `certificateSha256Digest` matching your signing cert, and no RASP tamper flags. Rooted/jailbroken and emulator traffic is a small, characterizable tail (developers, custom ROMs, security researchers); repackaged-client and active-instrumentation traffic is the anomaly against that baseline.

## Detection approach

Alert on attestation and tamper anomalies, enforced where it cannot be patched out: at the server. The high-signal conditions are (1) an integrity token with `UNRECOGNIZED_VERSION` or a `certificateSha256Digest` that is not your signing certificate, which is a repackaged or re-signed client; (2) `deviceIntegrity` that fails `MEETS_DEVICE_INTEGRITY`, or an App Attest assertion that fails server verification, correlated with sensitive actions (login, payment, token mint); (3) `appAccessRiskVerdict` showing `KNOWN_CAPTURING` / `KNOWN_OVERLAYS` / `KNOWN_CONTROLLING` or `playProtectVerdict` at `HIGH_RISK` during a high-value flow; (4) a device or account producing repeated RASP tamper events (Frida, debugger, root) or a cluster of pin-validation failures. Enforce by rejecting or step-up-challenging the request, not by trusting a client-returned boolean, and bind each verdict to a server-issued nonce and `requestHash` so a captured good token cannot be replayed.

Because the decisive evidence is attestation that you generate and verify in your own backend, a tuned UMBRASEC rule is not yet shipped for this threat, and the conditions above are expressed against your attestation and API telemetry rather than a shared log schema. For on-device design and verification, work from the **[OWASP MASVS](https://mas.owasp.org/MASVS/)** requirements and the **[OWASP MASTG](https://mas.owasp.org/MASTG/)** test cases. For server-side verdict handling follow the **[Google Play Integrity API documentation](https://developer.android.com/google/play/integrity)** and **[Apple App Attest / DeviceCheck documentation](https://developer.apple.com/documentation/devicecheck)**. For any host-side SIEM detections you choose to build from exported attestation logs, track community rules in the general **[SigmaHQ repository](https://github.com/SigmaHQ/sigma)**; note that mobile on-device telemetry is only lightly covered there, which is part of why enforcement belongs at your backend.

## Threat hunt

Hypothesis: a repackaged, instrumented, or intercepted client is reaching your API, or a sensitive action is succeeding without a valid attestation.

1. **Mine attestation verdicts over a long window.** Count requests by `appRecognitionVerdict` and `deviceRecognitionVerdict`; list every `UNRECOGNIZED_VERSION` and every `certificateSha256Digest` that is not your signing cert. Rank by account value and by whether a sensitive action followed.
2. **Find enforcement gaps.** Identify API routes that perform a high-value action (auth, payment, entitlement grant, secret retrieval) but accept requests with a missing, stale, or unverified integrity token or App Attest assertion. Those routes trust the client.
3. **Cluster tamper telemetry.** Group RASP events by device and by account over time; a single account hitting Frida/debugger/root flags across many sessions is an analyst or attacker, not benign root noise.
4. **Audit the shipped client, not just the live traffic.** In your own build, confirm `android:allowBackup="false"` and no `android:debuggable="true"` in the release manifest, confirm no secrets are hardcoded (string and resource scan), confirm the Network Security Config forbids cleartext and does not trust user-added CAs, and confirm sensitive values live in the Keystore/Keychain rather than SharedPreferences/`NSUserDefaults` or a plist. A finding here is a finding regardless of whether an attack has occurred.
5. **Verify pinning actually holds.** Confirm the pin set covers your current and backup leaf/intermediate keys with an expiry and a rotation plan, and that pin failures are reported, not silently swallowed.

## Hardening

Shrink the surface by designing the client as untrusted and enforcing trust server-side; detection is then a backstop. The primary framework for this work is **OWASP MASVS v2** (categories MASVS-STORAGE, MASVS-CRYPTO, MASVS-AUTH, MASVS-NETWORK, MASVS-PLATFORM, MASVS-CODE, MASVS-RESILIENCE, and MASVS-PRIVACY), verified with the **OWASP MASTG**, because the governance frameworks below have thin mobile-app-specific coverage.

Controls, in priority order:

- **Server-side attestation enforcement (the boundary).** Require a fresh, nonce-bound Play Integrity token or App Attest assertion on every sensitive request, verify it in the backend, and reject or step-up on failure. MASVS-RESILIENCE.
- **TLS certificate / public-key pinning + Certificate Transparency.** Pin leaf or intermediate public keys with a backup pin and an expiry; forbid cleartext; do not trust user-added CAs (Android Network Security Config, iOS App Transport Security). MASVS-NETWORK.
- **Secure storage.** Keep secrets and key material in the Android Keystore (StrongBox / hardware-backed where available) and the iOS Keychain with the Secure Enclave, using `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`; never in SharedPreferences, `NSUserDefaults`, plists, logs, or unencrypted databases. Set `android:allowBackup="false"` for sensitive data. MASVS-STORAGE, MASVS-CRYPTO.
- **Obfuscation and anti-tamper (RASP).** Apply R8/ProGuard shrinking and naming obfuscation (and a commercial obfuscator for high-value logic), and in-app protection that detects root/jailbreak, debuggers, emulators, and Frida/Xposed and reports to the server rather than only acting locally. MASVS-RESILIENCE, MASVS-CODE.
- **Attack-surface reduction in the manifest/config.** No `debuggable` release builds, minimize exported components and use permissions on them, set `FLAG_SECURE` on sensitive screens, enforce a minimum OS version, and keep SDKs and the platform patched.

Where governance mappings ground cleanly against the UMBRASEC [foundation guide](https://umbrasec.dev/guide/foundation.html), the platform-level controls map as below. Cells are blank where no single grounded control maps: the mobile-specific controls (pinning, Keystore/Keychain storage, obfuscation, RASP) are design properties of the app and are covered by OWASP MASVS above rather than by the Essential Eight, which is a Windows-estate model.

| Control | Essential Eight | ISO 27001:2022 | NIST CSF 2.0 |
|---|---|---|---|
| Enforce minimum OS version; keep app SDKs and the platform patched | Patch applications; Patch operating systems | A.8.8 Technical vulnerabilities | ID.RA; PR.PS Platform Security |
| Server-side attestation so only genuine, recognized app versions transact | Application control | A.8.19 Software on systems | PR.PS Platform Security |
| In-app hardening / RASP against tampering and malicious code in the app | User application hardening | A.8.7 Malware; A.8.19 Software on systems | PR.PS Platform Security |

## Honest limitations

Every client-side control runs on hardware the attacker controls, so with a rooted/jailbroken device and enough time, root/jailbreak detection, obfuscation, and pinning can all be patched or hooked out with Frida; treat them as cost-raising defense in depth, never as a trust boundary. That is precisely why server-side attestation is the boundary and why no client-returned "I am clean" boolean should gate a sensitive action. Attestation itself has gaps: Play Integrity's `MEETS_STRONG_INTEGRITY` needs recent hardware and a locked bootloader (older or non-GMS devices fall back to weaker verdicts or none), and attestation verdicts can be relayed or farmed on some devices, so treat them as strong risk signals and bind them to a server nonce rather than as proof. Root/jailbreak detection produces false positives on custom ROMs, developer devices, and legitimately rooted users, so enforcement must allow for a benign tail. Pinning complicates certificate rotation and incident response, so it needs a backup pin and a planned rotation or an app update can hard-fail every client. Finally, these are app-level controls: a compromised OS, a malicious keyboard or accessibility service, or screen-capture/overlay tooling operates below or beside the app, which is what the `appAccessRiskVerdict` signals exist to surface but cannot fully stop. Name those gaps rather than imply hardening makes the client trustworthy.

## ATT&CK and D3FEND

- MITRE ATT&CK (Mobile): [T1617 Hooking](https://attack.mitre.org/techniques/T1617/) (Frida/Xposed runtime instrumentation); [T1540 Code Injection](https://attack.mitre.org/techniques/T1540/) (injecting into the app process); [T1626 Abuse Elevation Control Mechanism](https://attack.mitre.org/techniques/T1626/) (running on a rooted/jailbroken device); [T1655 Masquerading](https://attack.mitre.org/techniques/T1655/) (repackaged / re-signed client); [T1638 Adversary-in-the-Middle](https://attack.mitre.org/techniques/T1638/) (pinning bypass and interception); [T1634 Credentials from Password Store](https://attack.mitre.org/techniques/T1634/) and [T1533 Data from Local System](https://attack.mitre.org/techniques/T1533/) (secrets pulled from insecure storage).
- MITRE D3FEND: [D3-PH Platform Hardening](https://d3fend.mitre.org/technique/d3f:PlatformHardening/) and [D3-ACH Application Configuration Hardening](https://d3fend.mitre.org/technique/d3f:ApplicationConfigurationHardening/) for RASP, attestation enforcement, and manifest/config reduction; [D3-MENCR Message Encryption](https://d3fend.mitre.org/technique/d3f:MessageEncryption/) and [D3-MAN Message Authentication](https://d3fend.mitre.org/technique/d3f:MessageAuthentication/) for TLS pinning of the API channel; [D3-FE File Encryption](https://d3fend.mitre.org/technique/d3f:FileEncryption/) for Keystore/Keychain-backed secure storage. D3FEND's mobile-specific coverage is thin, so OWASP MASVS is the operative framework here.
- Public rule and reference sources: [OWASP MASVS](https://mas.owasp.org/MASVS/) and [OWASP MASTG](https://mas.owasp.org/MASTG/); [Google Play Integrity API](https://developer.android.com/google/play/integrity); [Apple App Attest and DeviceCheck](https://developer.apple.com/documentation/devicecheck); [Android Network Security Configuration](https://developer.android.com/privacy-and-security/security-config); [SigmaHQ repository](https://github.com/SigmaHQ/sigma) for any host-side detections built from exported attestation logs.

**Scope note.** This is a defensive skill. It explains mobile app attacks only to the depth needed to harden a client and detect abuse of it, and deliberately ships no reverse-engineering, hooking, pinning-bypass, repackaging, or rooting tooling. UMBRASEC publishes defense, not offense.
