Most mobile security advice is a list of things to switch on. Encrypt storage, use HTTPS, add biometrics. All correct, all useless as a starting point, because a list cannot tell you which items matter for your product or what you are defending against.
This is the order we work in when we build something that holds real user data, drawn from lending, payments and healthcare adjacent apps we have shipped and had tested by third parties.
- Threat model before you pick controls. The list of what to protect determines everything after it.
- The device is hostile territory. Assume the binary is readable, the traffic is watchable and the OS may be rooted.
- Your API is the actual security boundary. Every rule enforced only in the app is decorative.
- Third party SDKs are the most underrated risk in mobile. You ship their code with your signature on it.
- Security that ends at launch is not security. Plan for certificate rotation, dependency patching and incident response.
Section 01
Start with a threat model, not a checklist
A threat model is three questions, and you can answer them in an afternoon on a whiteboard. What are we protecting? Who wants it? What happens if they get it?
A recipe app and a lending app both handle a login, but they are not the same problem. The recipe app is defending an email address. The lending app is defending identity documents and a path to somebody's bank account, against attackers who are financially motivated and patient. Those two products deserve very different budgets.
- List the data your app actually touches, including what you cache, what you log and what you send to analytics.
- Rank it by what a breach would cost: regulatory exposure, financial loss, reputational damage, physical safety.
- Name your realistic adversaries: opportunistic attackers on shared networks, a malicious user attacking your own API, someone with physical access to a lost phone.
- Decide explicitly what you are not defending against. A well resourced state actor is out of scope for most products, and pretending otherwise wastes money.

Section 02
Assume the device is hostile
This is the mental shift that matters most. Your app runs on hardware you do not control, owned by someone who may be your attacker.
Anyone can pull your APK or IPA, unzip it and read the strings. Anyone can run your traffic through a proxy. On a rooted or jailbroken device they can hook your functions at runtime and change what they return.
If a rule only exists in the mobile app, it does not exist. It is a suggestion the user can decline.
The practical consequences are simple. No API keys, secrets or private endpoints hardcoded in the bundle. No pricing, entitlement or permission logic that the server does not re verify. No trusting a client flag that says the user is an administrator.
Section 03
Authentication and session handling
Most real breaches are boring. They come through the front door because the session design was weak, not because someone broke your cryptography.
Get the token model right
- Short lived access tokens, ideally minutes, paired with a longer lived refresh token you can revoke server side.
- Rotate the refresh token on every use and invalidate the old one, so a stolen token stops working the moment the real user refreshes.
- Keep a server side session registry. If you cannot log a specific device out right now, you cannot respond to a compromise.
- Bind sessions to a device identifier and treat an unexpected change as a signal worth challenging.
Biometrics are a convenience layer
Face and fingerprint unlock protect a credential that is already on the device. They are not an authentication factor your server can verify, and the OS prompt can be bypassed on a compromised device. Use them to re open a session quickly, never as the only thing standing between a user and a money movement.
Section 04
Data at rest on the device
The rule is to store as little as possible, and to put whatever remains in the platform keystore rather than in application storage.
// Wrong. Readable on any rooted device, and backed up to the cloud.
final prefs = await SharedPreferences.getInstance();
await prefs.setString('auth_token', token);
// Right. Keychain on iOS, EncryptedSharedPreferences over the
// hardware keystore on Android.
const storage = FlutterSecureStorage(
aOptions: AndroidOptions(encryptedSharedPreferences: true),
);
await storage.write(key: 'auth_token', value: token);- Exclude sensitive files and preferences from iCloud and Android auto backup, or they leave the device you secured.
- Clear cached documents, generated PDFs and image thumbnails on logout. Statement caches are a common leak.
- Suppress the app snapshot the OS takes when backgrounding, so account balances do not sit in the task switcher.
- Treat the clipboard as public. Do not auto copy card numbers or one time codes to it.
Section 05
Data in transit
TLS everywhere is the baseline and it is not sufficient on its own, because TLS only proves you are talking to someone holding a certificate a trusted authority signed. On a device where an attacker has installed their own certificate, that is them.
Certificate pinning closes that gap by telling the app which certificate or public key to expect. It is worth doing for any app handling money or identity.
Section 06
The API is the real perimeter
Mobile security reviews focus on the app because that is the visible artefact. In our experience the serious findings are almost always on the server.
- Authorise every request against the authenticated user, not against an identifier the client sent. Passing a user id in the body and trusting it is the single most common mobile API flaw.
- Rate limit per account and per device, not only per IP address. Mobile networks share addresses heavily.
- Validate and constrain everything server side: amounts, quantities, dates, enum values, file types and sizes.
- Return the minimum. Do not ship an entire user object to the client because it was convenient for one screen.
- Make write endpoints idempotent so a retry cannot duplicate a transaction.
- Log security relevant events server side, where the client cannot suppress them.
Section 07
Third party SDKs and the supply chain
A typical mobile app ships analytics, crash reporting, an attribution SDK, a payments SDK and a support widget. Each one runs with your app's permissions, reads what your app reads, and goes out under your signing key. Your users cannot tell the difference and neither can a regulator.
- Audit what each SDK actually collects, not what its marketing page claims. Watch the traffic in a proxy.
- Pin dependency versions and review changes rather than floating on latest.
- Scrub personally identifiable and financial data at the point of capture, before it reaches crash reporting. Redacting in the vendor dashboard is too late.
- Remove SDKs nobody uses. Every one is permanent attack surface for a feature somebody wanted once.
Section 08
Release hardening
These are cheap, they happen at build time, and they raise the cost of casual reverse engineering. None of them stop a determined attacker, which is the point: they are speed bumps, not walls, and should never be the reason a control is missing on the server.
- Enable code obfuscation and strip debug symbols from release builds, archiving the symbol files so you can still read crash reports.
- Strip all logging from production. Debug logs of request bodies are a gift to anyone reading the device log.
- Detect root, jailbreak and repackaging, report the signal to your backend, and decide there what to do about it.
- Block screenshots and screen recording on credential, document and statement screens.
- Verify the app signature at runtime so a repackaged clone fails against your API.
Section 09
Security does not end at launch
The most common failure we are called in to fix is not a weak app. It is a strong app that nobody maintained for eighteen months.
- Patch dependencies on a schedule, not when something breaks. Most mobile vulnerabilities arrive through libraries.
- Diarise certificate expiry well ahead of the date, especially where pinning is in play.
- Run an external penetration test before launch and after any significant change to authentication or payments.
- Give users a way to report a vulnerability and a person who reads it.
- Write the incident plan while nothing is on fire: how to revoke sessions, force an update and communicate.
Frequently asked questions
How much should mobile app security cost as a share of the build?
For a product handling payments, identity or health data, plan for roughly 10 to 15 percent of engineering effort plus an external penetration test. For an app holding little beyond an email address it is considerably less. The threat model is what tells you where on that range you sit.
Is a native app more secure than a cross platform one?
No. The framework is close to irrelevant. Protection comes from the platform keystore, transport security, and a backend that re verifies every decision. We have seen weak native apps and strong Flutter apps, and the difference was always engineering discipline rather than the toolkit.
Do we really need certificate pinning?
If the app moves money or handles identity documents, yes. For lower risk products plain TLS with strict transport settings is often a reasonable position. If you do pin, commit to a rotation plan and backup pins, because a mishandled renewal takes down every installed copy at once.
Can we rely on the app stores to catch security problems?
No. Apple and Google review for policy compliance and obvious malware, not for whether your API authorises requests correctly. Store approval is not a security assessment and should never be treated as one.


