This is a worked threat model for a mobile banking-style app: an Android and iOS client, a backend API, an OAuth 2.0 / OpenID Connect identity provider, a core ledger and push notifications, with payment initiation that requires step-up approval. Model it by drawing the data flows and trust boundaries, then walking STRIDE over each element and flow: token and data theft from the device, reverse engineering and repackaging, TLS interception, deep-link hijacking, overlay and screen-capture malware, SIM swap and OTP interception, transaction tampering and replay, approval fatigue, and backend API abuse. The mitigations are the platform keystores, attestation (Play Integrity, App Attest), transport hardening, device-bound transaction signing and, decisively, server-side authorization and limits. The controls map to OWASP MASVS v2.
- The device is outside your trust boundary: a rooted or malware-infected phone can read anything the app can, so secrets and decisions cannot live only on the client.
- Protect tokens and keys with the platform key stores (iOS Keychain and Secure Enclave; on Android, keys in the Android Keystore or StrongBox that encrypt the tokens), not in plain app files or shared preferences (MASVS-STORAGE, MASVS-CRYPTO).
- Attestation (Play Integrity, App Attest) raises the cost of tampered clients and emulators but is a signal, not an authorization check; verify it server-side (MASVS-RESILIENCE).
- Keep transport strict: TLS with the platform defaults, no cleartext, and pin or use the managed network config, weighing pinning's operational cost (MASVS-NETWORK).
- Bind high-value actions to a device key and show the exact transaction in the approval, so phishing and approval fatigue can't move money (MASVS-AUTH).
- Every authorization, limit and anti-fraud decision is enforced on the backend; client checks are UX, not security.
| Setting | Value | Note | Source |
|---|---|---|---|
| Token and key storage | Android Keystore (StrongBox where available); iOS Keychain + Secure Enclave | Hardware-backed keys that don't leave the secure element. The Android Keystore holds keys, not tokens: encrypt tokens with a Keystore key, never store them in plain files or shared preferences. | OWASP MASVS-STORAGE-1, MASVS-CRYPTO-1 |
| Client integrity | Play Integrity API (Android); App Attest / DeviceCheck (iOS), verified server-side | A signal that raises cost; never the sole gate for a sensitive action. | OWASP MASVS-RESILIENCE; Android and Apple docs |
| Transport | TLS with platform defaults; no cleartext; pinning or managed network config | Android Network Security Config and iOS App Transport Security; pinning adds rotation cost. | OWASP MASVS-NETWORK-1, MASVS-NETWORK-2 |
| Deep links | Verified Android App Links and iOS Universal Links | Unverified custom schemes can be claimed by another app and used to hijack flows. | OWASP MASVS-PLATFORM-1 |
| Sensitive screens | FLAG_SECURE (Android); block screen capture / hide in app switcher (iOS) | Stops screenshots and screen recording of balances and OTPs. On Android 12 and later, setHideOverlayWindows(true) also hides other apps’ overlays. | OWASP MASVS-PLATFORM-3 |
| High-value action approval | Device-bound signature (passkey/FIDO or Keystore key) over the exact transaction | Show payee and amount in the prompt; do not rely on SMS OTP, which SIM swap defeats. | OWASP MASVS-AUTH-2, MASVS-AUTH-3 |
| Authorization and limits | Enforced on the backend for every request | Object-level checks, velocity and amount limits, and anti-fraud run server-side. | OWASP API Security Top 10 2023 API1/API6 |
- Walk each element and flow in the diagram against STRIDE and confirm every identified threat has a mitigation with an owner and a test.
- On a rooted device and an emulator, confirm the app's tokens and keys are not readable from files or shared preferences and that attestation fails closed server-side.
- Intercept traffic with a proxy and a user-installed CA and confirm the app refuses to connect (pinning or network config), then confirm a certificate rotation plan exists.
- Attempt to invoke the app through an unverified deep link and a second app claiming the same scheme, and confirm sensitive flows do not start.
- Confirm a payment approval shows the exact payee and amount, is bound to a device key, and that the backend rejects a replayed or tampered transaction and enforces limits regardless of the client.
- Test SIM-swap and OTP-interception scenarios against account recovery and step-up, and confirm they do not grant money movement on their own.
- Applies to
- Android Keystore/StrongBox, Network Security Config, App Links, Play Integrity API
- iOS Keychain + Secure Enclave, App Transport Security, Universal Links, App Attest / DeviceCheck
- OWASP MASVS v2
- OWASP Mobile Top 10 2024
Mobile apps that handle money or other high-value actions on Android and iOS with a backend API and an identity provider. Platform APIs are current as of October 2026.
- Part of
- Workflow: Review a design
Tested fixes: the vulnerable code, the fix, a plausible fix that still fails, and the regression tests that tell them apart.
Learning goals and prerequisites
What you will learn
- Draw the data flows and trust boundaries of a mobile banking app.
- Enumerate STRIDE threats on the device, the transport and the backend.
- Choose mitigations and map them to OWASP MASVS v2 controls.
- Separate client-side hardening from the server-side checks that actually enforce decisions.
Before you start
Mobile and backend engineers and security reviewers doing a design review of a high-value mobile app.
- Read a data-flow diagram and know what a trust boundary is.
- Know the basics of mobile app, OAuth and API architecture.
Refresh first: Choose a threat modeling method and turn its findings into tests
Go to the guided practiceData flows and trust boundaries of a mobile banking app
- Sign inThe app runs an OAuth 2.0 / OIDC authorization-code flow with PKCE against the identity provider and protects the tokens with the platform key store (iOS Keychain; on Android, encrypted with a Keystore key).
- Call the APIThe app calls the backend over TLS with platform defaults and no cleartext, pinned where the rotation cost is acceptable, sending an access token; the backend authorizes every request.
- Read account dataThe backend reads balances and history from the core ledger, enforcing object-level authorization for the signed-in customer.
- Approve a paymentA payment requires step-up: the app signs the exact transaction with a device-bound key and the backend verifies it, enforcing limits and anti-fraud.
- NotifyThe backend sends a push notification with no sensitive content; opening it re-authenticates in the app.
The app is a retail banking client on Android and iOS. It signs the user in through an OAuth 2.0 / OpenID Connect provider using the authorization-code flow with PKCE, stores the resulting tokens in the platform keystore, and calls a backend API over TLS. The backend reads balances and transaction history from a core ledger and initiates payments, which require a step-up approval. Push notifications tell the user about activity but carry no sensitive content.
State the assumptions before modeling. The user's phone may be rooted, jailbroken, running in an emulator, or infected with malware that has accessibility or overlay permissions. The network may be hostile, including a proxy with a user-installed CA. The identity provider, backend and ledger are inside the bank's trust boundary and are modeled elsewhere; this model focuses on the client, the transport and the client-facing API.
The diagram above shows the flows. The table enumerates the elements and the trust boundary each sits behind, which is what STRIDE is applied to.
| Element | Data it handles | Trust boundary |
|---|---|---|
| Mobile app | Tokens, cached balances, device key, PII | Untrusted device |
| Keystore / Keychain | Refresh token, device-binding key | Hardware-backed on device |
| App-to-API channel | Access token, requests, responses | Public network (untrusted) |
| Identity provider | Credentials, tokens, MFA state | Server trust boundary |
| Backend API | Authorization, limits, anti-fraud | Server trust boundary |
| Core ledger | Balances, transactions | Server trust boundary |
| Push channel | Notification metadata only | Third-party, untrusted |
Most mobile-specific risk is on the device, where the attacker may fully control the platform. Walk STRIDE over the app and its stored data.
| Threat (STRIDE) | Example | Mitigation (MASVS) |
|---|---|---|
| Information disclosure | Tokens or PII read from app files on a rooted device | Store secrets in Keystore/StrongBox and Keychain+Secure Enclave; store the minimum (MASVS-STORAGE-1, MASVS-CRYPTO-1) |
| Tampering | Repackaged or hooked app, or an emulator | Play Integrity / App Attest verified server-side; obfuscation and anti-hooking as cost, not a gate (MASVS-RESILIENCE) |
| Spoofing | Overlay or accessibility malware captures the PIN or an OTP | FLAG_SECURE and screen-capture protection on sensitive screens; detect overlays (MASVS-PLATFORM-3) |
| Elevation of privilege | Another app claims the custom URL scheme to hijack a flow | Verified App Links / Universal Links, not custom schemes (MASVS-PLATFORM-1) |
| Information disclosure | Balance shown in the app switcher or a screenshot | Hide sensitive content when backgrounded; block screenshots (MASVS-PLATFORM-3) |
The channel between the app and the backend is on a public network. The main threats are interception and modification.
- Information disclosure and tampering: a man-in-the-middle with a user-installed CA reads or changes traffic. Mitigate with TLS using platform defaults, no cleartext (Android Network Security Config, iOS App Transport Security), and certificate or public-key pinning where the operational cost of rotation is acceptable (MASVS-NETWORK-1, MASVS-NETWORK-2).
- Spoofing: a phishing site imitates the app's web views. Keep authentication in the system browser or an authenticated web view, validate the OIDC issuer and audience, and never accept tokens the app did not request.
- Replay: a captured request is resent. Use short-lived access tokens, TLS, and per-transaction signatures with a nonce for money movement (below).
The backend is where decisions are actually enforced, so client-reachable API threats matter even though the server is inside the trust boundary. The mobile client can be fully controlled by an attacker, so treat every request as attacker-shaped.
- Elevation of privilege / information disclosure: broken object-level authorization lets one customer read or move another's money by changing an id. Enforce object-level authorization on every request against the authenticated subject (API1:2023); see /domains/appsec/authorization/object-level-authorization/.
- Tampering with business flows: automated abuse of a sensitive flow such as transfers or rewards. Apply velocity and amount limits and anti-fraud server-side (API6:2023).
- Spoofing: a stolen or replayed token. Bind tokens to the client where possible, keep them short-lived, and require step-up for high-value actions.
- Repudiation: a disputed payment. Keep an append-only audit log of the signed transaction and the approval.
Not every attack goes through the app. SIM swap lets an attacker receive SMS one-time codes, and OTP interception and phishing capture codes in transit, so SMS OTP is a weak second factor for authorizing money movement. Model recovery and step-up explicitly: prefer passkeys or a device-bound key over SMS, require re-authentication for sensitive changes, and ensure that compromising the phone number alone does not grant payment authority. The account-takeover guide at /domains/identity-crypto/authentication/mfa-account-takeover/ covers these paths.
The decisive control against phishing, malware and approval fatigue is to bind a high-value action to a key on the device and to the exact transaction. When the user approves a payment, the app signs a structure containing the payee, amount and a server nonce with a key in the Keystore or Secure Enclave (a passkey/FIDO credential works well), and the approval prompt shows the payee and amount so the user is approving what they see. The backend verifies the signature, checks the nonce to stop replay, and enforces limits and anti-fraud regardless of what the client claims. A blind push-approval that says only approve or deny trains users to tap approve; show the details.
A threat model is honest about what remains. On a fully compromised device, the attacker can observe the user and the app in real time; device-binding and server-side limits contain the damage but do not eliminate it. Attestation can be bypassed by determined attackers, so it reduces fraud volume rather than guaranteeing an authentic client. Pinning protects the channel but adds a rotation obligation; a bad rotation can brick the app, so plan it. Write these down with the compensating controls (limits, anti-fraud, monitoring) and revisit them when the platform or the feature set changes.
Re-run the model when the architecture changes: a new high-value feature, a new third-party SDK, a change to the identity provider, or a platform API deprecation. Track each threat to a control, a MASVS requirement and a test, and tie the tests into CI (the methods guide at /domains/appsec/threat-modeling/threat-modeling-methods/ covers per-story threat modeling). Map deep dives to OWASP MASVS v2 and the MASTG test cases, and watch the OWASP Mobile Top 10 2024 for new classes.
Guided practice
You are reviewing the mobile banking app before a release that adds instant payments. The design notes below are from the team. For each, name the STRIDE category and MASVS area, say what is wrong, and give the fix.
Supplied scenario material
N1 The refresh token is stored in Android SharedPreferences and an iOS plist file "for reliability."
N2 Payments are approved by an SMS one-time code sent to the registered number.
N3 The app opens a payment confirmation via myapp://pay?to=...&amount=... (a custom scheme).
N4 The app disables certificate validation in debug builds, and the debug flag ships enabled.
N5 The approval screen says "Approve this payment?" with Approve / Deny buttons and no details.
N6 The backend checks that the account id in the request belongs to a valid customer, then returns the data.
N7 Account balances are visible in the app switcher thumbnail.1. Fix token storage
Classify N1 and give the fix.
Expected result and reasoning
Information disclosure, MASVS-STORAGE/CRYPTO:
SharedPreferencesand plist files are readable on a rooted/jailbroken device and in backups. Encrypt the refresh token with an Android Keystore key (StrongBox if available), keep it in the iOS Keychain with Secure Enclave protection, and keep access tokens short-lived in memory.2. Replace SMS approval
Classify N2 and give the fix.
Expected result and reasoning
Spoofing, MASVS-AUTH: SMS OTP is defeated by SIM swap and interception. Approve payments with a device-bound signature (passkey/FIDO or a Keystore/Secure Enclave key) over the exact transaction, verified server-side.
3. Fix the deep link
Classify N3 and give the fix.
Expected result and reasoning
Elevation of privilege / spoofing, MASVS-PLATFORM-1: a custom scheme can be claimed by another installed app to hijack the flow. Use a verified Android App Link / iOS Universal Link, and never start a payment flow from an unauthenticated deep link.
4. Fix TLS
Classify N4 and give the fix.
Expected result and reasoning
Information disclosure / tampering, MASVS-NETWORK: shipping with validation disabled allows man-in-the-middle. Never disable validation in shipped builds; strip debug flags in release, enforce the Network Security Config / ATS, and pin with a rotation plan.
5. Fix the approval and the authorization
Classify N5 and N6 and give the fixes.
Expected result and reasoning
N5 is approval fatigue (MASVS-AUTH): show payee and amount and bind the signature to them. N6 is broken object-level authorization (API1:2023): checking that the account is a valid customer is not checking it belongs to the authenticated caller; authorize the object against the signed-in subject.
The exercise reasons about design notes on paper. It does not run the app on real devices, test attestation bypass, or exercise the backend; a real review needs device testing, a proxy interception test, and backend authorization tests.
Save a draft on this device to resume later. Automatic saving begins after you choose Save. Drafts are never sent by this application; use synthetic data.
No draft saved on this device yet.
Sources & further reading
The standards, specifications and documentation behind the guidance above. Each is the evidence for a claim on this page; consult it for detail beyond the defaults and versions listed here.
- OWASP MASVS v2 (Mobile Application Security Verification Standard)
- OWASP Mobile Application Security Testing Guide (MASTG)
- OWASP Mobile Top 10 (2024)
- Android: Play Integrity API
- Android: Network Security Configuration
- Android: verify App Links
- Apple: establishing your app's integrity (App Attest / DeviceCheck)
- Apple: supporting Universal Links in your app
- OWASP API Security Top 10 2023: API1 Broken Object Level Authorization
- Microsoft STRIDE threat model
Reference information
- SDLC phase
- Design
- Role
- AppSec engineers
- OWASP MASVS v2 controls
- MASVS-STORAGE-1 · MASVS-CRYPTO-1 · MASVS-AUTH-2 · MASVS-AUTH-3 · MASVS-NETWORK-1 · MASVS-NETWORK-2 · MASVS-PLATFORM-1 · MASVS-PLATFORM-3 · MASVS-RESILIENCE-1