Mobile security

OWASP Mobile Top 10, translated for Flutter teams

In one paragraph

In Flutter apps, the OWASP Mobile Top 10 risks concentrate in four places: secrets compiled into the Dart bundle, data written through the default preferences plugin instead of secure storage, missing certificate pinning in the HTTP client, and authorisation enforced in the widget tree instead of on the API.

Cross-platform teams read the OWASP Mobile Top 10 and correctly conclude that it applies to them, then incorrectly conclude that the platform handles most of it. Flutter handles rendering. Everything on this list is still yours.

Improper credential usage

The Flutter-specific version: a key passed through --dart-define, then read into a const at build time. Developers assume the value is injected at runtime. It is compiled in, and recoverable from the snapshot.

  • Fix: proxy third-party calls through your own backend so the key never ships.
  • Where a public key is unavoidable, restrict it server side by package name, signature and referrer.
  • Never gate a paid feature on a client-side flag — that is an entitlement check, and it belongs on the server.

Inadequate supply chain security

A typical Flutter project pulls in fifty to eighty transitive packages, many maintained by one person. That is normal and workable, but it needs the same discipline as any dependency tree.

  • Pin versions and review the diff on upgrades of anything that touches storage, crypto or networking.
  • Prefer packages with recent commits, several maintainers and a published changelog.
  • Run dependency scanning in CI on both pubspec and the native Gradle and CocoaPods trees, which people forget exist.

Insecure authentication and authorisation

The pattern I find most often: the app fetches a user object containing a role field, stores it, and renders admin widgets when the role matches. The API then trusts whatever the app asks for, because "only admins can see that screen".

  • Enforce every permission on the endpoint, per object, per request.
  • Treat the role in the app purely as a UI hint with no security meaning.
  • Test with two accounts and swap identifiers — if the response changes, you have a finding.

Insufficient input and output validation

WebViews are the usual entry point in Flutter apps: a help centre, a payment page, an offer wall. If a WebView loads a URL built from anything user-controlled, or has JavaScript channels registered without origin checks, native functionality is one crafted link away.

Insecure communication

Flutter's HTTP clients validate certificates by default, which is a good baseline and where most teams stop. Pinning must be added explicitly, and the badCertificateCallback that someone added while debugging on a corporate network must not reach production.

// Never ship this. It disables certificate validation entirely.
HttpOverrides.global = _DevOverrides(); // returns true for every cert

// Ship pinning instead, with a rotation plan for the pinned keys.

Inadequate privacy controls

  • Crash reporters and analytics SDKs frequently capture request bodies and screen contents — configure redaction explicitly.
  • Set FLAG_SECURE on screens showing balances, documents or health data.
  • Clear caches, cookies and stored files on logout, not just the auth token.

Insufficient binary protections

Obfuscation with --obfuscate --split-debug-info raises the cost of reversing. It is worth enabling and worth nothing on its own — treat it as friction, and keep the symbol files so your crash reports remain readable.

Security misconfiguration

  • allowBackup left true, exporting your app's data directory to cloud backup.
  • Debug-signed builds distributed to testers with debugging enabled.
  • Exported activities from plugin manifests that the app never audited after merge.

Insecure data storage

shared_preferences is not encrypted. On a rooted device, or through a backup, its contents are plain text. Use flutter_secure_storage or an equivalent that maps to Keystore and Keychain, and store as little as possible.

Insufficient cryptography

Hand-rolled AES helpers with a static IV, a key derived from the package name, or ECB mode picked because it was the shortest example on the internet. Use platform primitives, generate keys in the secure enclave, and if the code contains the word "custom" next to "encryption", schedule a review.

Where to start

  1. Grep the build output for secrets. Fix what you find.
  2. Move every token and PII write to secure storage.
  3. Write the authorisation matrix for your API and test it with two accounts.
  4. Then, and only then, worry about obfuscation and root detection.

Want this tested properly?

Scope and a fixed quote within two working days. First consultation is free.