Join our Newsletter — 33% off our NHI Course
Home› Guides› Flutter and React Native Security Guide: Cross-Platform Storage,…
Guide Application Security

Flutter and React Native Security Guide: Cross-Platform Storage, Bridges and Obfuscation

← All guides
By NHI Mgmt Group, edited by Lalit Choda Updated 27 October 2026 14 min read
On this page

Cross-platform frameworks let one team ship to iOS and Android from a single codebase, but they also move security decisions out of the native platform guidance that most mobile checklists assume. A React Native app ships its logic as a JavaScript or Hermes bytecode bundle inside the package; a Flutter app ships compiled Dart in a native library; both reach the operating system through bridges and plugins written by third parties; and both are often updated over the air. This guide covers what is different about securing Flutter and React Native apps: where tokens and keys should live, what obfuscation does and does not buy, how bridges, WebViews and deep links widen the attack surface, how over-the-air updates must be signed, and how to detect and measure it. It is written for mobile leads, application security engineers and penetration testers; the general mobile picture is in the Mobile App Security Guide.

Key takeaways

  • Everything in the bundle is public. Both the React Native and Flutter documentation say not to store secrets in the app; Hermes bytecode and Dart AOT code are readable with freely available tools, so any API secret shipped in the app must be treated as disclosed.
  • Use the platform keystores, never plain key-value storage. React Native Async Storage and Flutter's shared_preferences are unencrypted; tokens belong in the iOS Keychain and Android Keystore through a maintained wrapper with explicit accessibility settings.
  • Obfuscation is a speed bump. Flutter's own documentation states that --obfuscate renames symbols, does not encrypt resources and does not protect against reverse engineering.
  • Bridges and plugins are your new attack surface. Platform channels, native modules and WebView message handlers accept untrusted input and run with the app's full permissions; validate everything that crosses them.
  • Over-the-air updates are code delivery. Sign them end to end and protect the signing key as you would a store release key, or an update channel becomes a remote code execution path.

How identity and access work in Flutter and React Native

Neither framework has its own identity layer. Sign-in, tokens and authorisation decisions are implemented by the app team and the backend, which is exactly why they go wrong. The safe pattern is the same as for native apps: OAuth 2.0 authorisation code flow with PKCE in the system browser, short-lived access tokens, a refresh token held in hardware-backed storage, and every authorisation decision made by the server.

  • OAuth in the system browser: React Native's security page recommends PKCE and points to react-native-app-auth, which wraps the AppAuth-iOS and AppAuth-Android libraries; Flutter teams typically use a plugin that drives ASWebAuthenticationSession and Custom Tabs. Never sign users in through an embedded WebView you control. See the OAuth for Native and Mobile Apps Guide.
  • Redirects and deep links: the React Native documentation warns that any app can register the same custom URL scheme and that iOS gives the user no choice when two apps claim one, so tokens must never travel in deep links. Prefer Universal Links and Android App Links for redirects. See the Mobile Deep Link and App Link Security Guide.
  • Local authentication: biometrics should unlock a key in the keystore (a cryptographic binding), not flip a boolean in Dart or JavaScript that a hooking tool can patch. Expo SecureStore's requireAuthentication option, for example, ties the value to device authentication, and a new enrolled fingerprint or face invalidates it.
  • Authorisation stays on the server: hiding a button in the UI is not access control. Treat every API call as if it came from a modified client.

Where to store what

Storage optionFrameworkEncrypted at restUse for
Async Storage (@react-native-async-storage/async-storage)React NativeNoNon-sensitive preferences and cached state only
shared_preferences / NSUserDefaults / SharedPreferencesBothNoNon-sensitive settings
iOS Keychain (via react-native-keychain, expo-secure-store, flutter_secure_storage)BothYes, hardware-backed keysRefresh tokens, session secrets, private keys
Android Keystore with encrypted preferences (same wrappers)BothYes, key in KeystoreRefresh tokens, session secrets
In-memory onlyBothNot persistedAccess tokens, PKCE verifier, decrypted data
SQLite, Realm, Hive and similar databasesBothOnly if you add encryption (for example SQLCipher) with a key from the keystoreOffline data that must be protected

Set accessibility deliberately. Expo SecureStore defaults to WHEN_UNLOCKED; WHEN_UNLOCKED_THIS_DEVICE_ONLY stops a value migrating to a new device through a backup, which is usually right for refresh tokens. On Android, exclude secure-store preferences from Auto Backup, because restored entries cannot be decrypted once the Keystore key is gone (Expo does this automatically unless you supply a custom backup configuration). Wrapper libraries are community code: the React Native documentation says to use them at your own risk, so pin versions and review them as you would any security dependency.

Admin roles and privileged access

In a mobile app the privileged access that matters is less about in-app admin screens and more about who can change what runs on users' phones.

  • Hidden admin and debug features: developer menus, feature-flag overrides, staging environment switchers and "god mode" accounts compiled into release builds are found quickly by anyone who unpacks the bundle. Strip them at build time (Dart kReleaseMode or --dart-define constants; JavaScript __DEV__ checks that the bundler removes), and enforce admin capability on the server.
  • Store and signing accounts: App Store Connect, Google Play Console and the app signing keys can push a new binary to every user. Require phishing-resistant MFA, least-privilege roles and Play App Signing or equivalent so the upload key can be reset if lost. See the Code Signing Security Guide.
  • Over-the-air update channels: services such as EAS Update deliver new JavaScript without a store review. Expo's end-to-end code signing (RSA with SHA-256, rsa-v1_5-sha256) embeds a certificate in the native build, signs updates locally with a private key that should live in a KMS or password manager, and makes the client reject any update whose signature is invalid. Expo documents it as available on its Production and Enterprise plans; whichever OTA service you use, confirm it supports client-side signature verification. Flutter has no first-party OTA mechanism; third-party code-push services for Flutter need the same scrutiny.
  • CI/CD identities: the pipeline that builds, signs and uploads releases holds the most powerful credentials in the mobile estate. See the CI/CD Pipeline Identity Security Guide.

Integrations, API tokens and service accounts

Cross-platform apps tend to accumulate third-party SDK keys: maps, analytics, crash reporting, payments, push, Firebase and AI APIs. The research record is clear. Cybernews found at least one hard-coded secret in 71 percent of 156,080 iOS apps it analysed; see the iOS Apps Leaking Hard-Coded Secrets 2025 page. Symantec found hard-coded AWS credentials in 1,859 apps, including one identity SDK whose embedded keys were reused across five banking apps; see Mobile Apps Hard-Coded AWS Keys 2022.

Key typeSafe in the app?Required handling
Publishable or client keys designed to be public (Stripe publishable key, Firebase web config, maps keys)Yes, if restrictedRestrict by app package, bundle ID and signing certificate, by API and by quota; pair with app attestation where offered
Secret API keys (payment secret keys, LLM API keys, cloud access keys)NoMove behind your backend or a serverless proxy that holds the key and authenticates the user, as the React Native documentation recommends
react-native-config / .env values and Dart --dart-define valuesOnly non-secretsCompiled into the bundle; React Native's documentation states these tools are for environment-specific values, not secrets
Per-user tokensYes, in the keystoreShort-lived access token in memory, rotated refresh token in Keychain or Keystore
  • Backend for the mobile app: a thin backend that holds partner secrets and calls third-party APIs on the user's behalf is the only way to keep a secret secret. Rate limit it per user so it does not become a free proxy.
  • App attestation: Apple App Attest and the Google Play Integrity API let the backend check that a request comes from your unmodified app on a genuine device. Use attestation to raise cost and reduce abuse, never as authentication of the user. Firebase App Check wraps both for Firebase services; see the Firebase Credentials Security Guide.
  • Do not leak to telemetry: the React Native documentation warns specifically against sending tokens and personal data to crash and monitoring services and against persisting a whole Redux state tree that contains sensitive fields. Scrub breadcrumbs and network logs in Sentry, Crashlytics and similar SDKs.

Bridges, plugins and WebViews

  • Flutter platform channels: MethodChannel and BasicMessageChannel pass messages between Dart and native code using codecs such as StandardMessageCodec; Flutter's documentation describes BasicMessageChannel as not type safe. Generate channels with Pigeon for typed interfaces, and validate arguments on the native side as you would an API.
  • React Native native modules: every method exposed to JavaScript is callable by any JavaScript that runs in the app, including code from a compromised dependency or an injected OTA update. Expose narrow, purpose-built methods (sign this payload) rather than generic ones (read any file).
  • WebViews: react-native-webview defaults originWhitelist to any http or https origin and javaScriptEnabled to true. Restrict origins, keep allowFileAccess, allowFileAccessFromFileURLs and allowUniversalAccessFromFileURLs false, keep mixedContentMode at never, do not set setSupportMultipleWindows to false (documented as exposing CVE-2020-6506), and treat onMessage data as untrusted strings. onShouldStartLoadWithRequest is not called for the first load on Android, so it cannot be the only navigation check. See the Mobile WebView Security Guide.
  • Plugin supply chain: pub.dev and npm packages bring native code with them. Pin versions with pubspec.lock and package-lock.json, review native permissions a plugin adds to AndroidManifest.xml and Info.plist, and watch for install scripts in npm dependencies. See the OWASP Mobile Top 10 M2 Inadequate Supply Chain Security Guide.

Logging and detection

Mobile apps run on devices you do not control, so most detection happens at the backend and in the build pipeline.

SignalWhere to collect itWhat it indicates
Attestation failures and verdict trendsBackend logs of App Attest and Play Integrity checksRepackaged or tampered clients, emulator farms
Use of restricted client keys from unexpected packages or IPsCloud and SDK provider consolesKey extracted from the app and reused
Secrets found in release artefactsCI scan of the APK, AAB and IPA (bundle, libapp.so, assets)A secret about to ship
Refresh token reuse after rotationAuthorisation serverToken copied from a device
OTA updates published outside the release processUpdate service audit logCompromised publishing credential
Crash reports containing tokens or personal dataCrash and monitoring SDK data scansLogging leak (MASVS-STORAGE-2)

Metrics:

  • Secrets detected per release build (target zero, gate the pipeline on it).
  • Percentage of sensitive values stored through a keystore wrapper rather than plain storage, from code review or static analysis.
  • Share of API traffic carrying a valid attestation verdict.
  • Age of the Flutter SDK or React Native version in production against the current stable release.
  • Percentage of OTA updates verified by client-side signature checks (target 100).

Hardening baseline

  • Keep the framework current: Flutter publishes security fixes for the current stable branch only and advises keeping the SDK and dependencies updated rather than running private forks; React Native fixes likewise land in supported releases. Budget for regular upgrades (MASVS-CODE-3).
  • Release build settings: Flutter flutter build apk --obfuscate --split-debug-info=<dir> (supported for Android, iOS and desktop targets, not web, where release builds are minified instead), keeping the symbol files private for flutter symbolize; React Native release builds with Hermes bytecode, R8 or ProGuard for the Android native layer, and no source maps shipped in the package.
  • Network: HTTPS only, enforced by Android Network Security Configuration (cleartextTrafficPermitted="false") and iOS App Transport Security without broad exceptions. Pin only if you can rotate: the React Native documentation notes that pinned certificates expire and old app versions stop working. Pinning to a public key with a backup pin reduces that risk (MASVS-NETWORK-2).
  • Storage: keystore-backed wrappers with explicit accessibility, encrypted local databases, backup exclusions, no sensitive data in logs or in persisted state stores.
  • Runtime protection where the risk justifies it: root and jailbreak detection, hooking and debugger detection, and integrity checks raise the cost of attack (MASVS-RESILIENCE). They are bypassable; use them for high-value apps such as banking and keep the real controls server-side.

Rolling it out

  1. Scan what you ship: unpack the current production APK, AAB and IPA, extract the bundle or Dart library strings and run secret scanning. Rotate anything found before fixing the code.
  2. Move secrets server-side: introduce the backend proxy for secret keys and restrict every remaining public key by package and signing certificate.
  3. Migrate storage: move tokens from Async Storage or shared_preferences to a keystore wrapper. Ship a migration that reads the old value once, writes it securely and deletes it; roll back by keeping the read path for one release.
  4. Fix sign-in: move to system-browser OAuth with PKCE and claimed HTTPS redirects; pilot with a percentage rollout and monitor sign-in success.
  5. Sign OTA updates, then rotate the update signing key into a KMS and remove publishing rights from individual developers.
  6. Add attestation in monitor mode, log verdicts for several weeks to measure false positives, then enforce on high-risk endpoints first.
  7. Gate CI: secret scanning, dependency audit, and a MASVS-based test pass before each store release. See the Mobile App Security Testing Guide.

Decision guidance: choose Flutter or React Native on product grounds; neither is meaningfully more secure. The security differences are operational: React Native teams must govern a large npm dependency tree and, often, OTA updates; Flutter teams must govern native plugin code and track a fast-moving SDK. Small teams should prefer managed, well-maintained wrapper libraries and the platform defaults; regulated apps (banking, health) should add attestation, runtime protection and an independent MASVS L2 assessment.

Framework mapping

Control areaOWASP MASVSNIST SP 800-53 Rev. 5MITRE ATT&CK (Mobile)
Keystore-backed token storageMASVS-STORAGE-1, MASVS-CRYPTO-2SC-28, IA-5T1409 Stored Application Data
System-browser OAuth, claimed redirectsMASVS-AUTH-1, MASVS-PLATFORM-1IA-2T1635.001 URI Hijacking
WebView and bridge hardeningMASVS-PLATFORM-2, MASVS-CODE-4SI-10n/a
Signed OTA updates and store releasesMASVS-CODE-2CM-14, SI-7T1407 Download New Code at Runtime
Dependency and plugin governanceMASVS-CODE-3SR-3, SR-4T1474 Supply Chain Compromise
Transport security and pinningMASVS-NETWORK-1, MASVS-NETWORK-2SC-8n/a

Common misconfigurations

  • Secret keys in .env: a payment or LLM API secret placed in react-native-config or a --dart-define value ends up as a plain string in the release bundle.
  • Tokens in Async Storage or shared_preferences: unencrypted on disk and included in device backups; the most common finding in cross-platform app assessments.
  • Treating obfuscation as protection: relying on --obfuscate or Hermes bytecode to hide keys or business logic; neither framework claims that.
  • Login in an embedded WebView: the app can read the user's credentials, single sign-on cookies are not shared, and many identity providers block it.
  • Open WebView configuration: default originWhitelist, file access enabled for a local HTML feature, and an onMessage handler that calls a powerful native module.
  • Unsigned OTA updates with a shared publishing token: one leaked CI token can push code to every installed app without a store review.

Practitioner checklist

  • Scan every release artefact for secrets and fail the build on findings.
  • Move secret API keys behind a backend; restrict publishable keys by package, bundle ID and certificate.
  • Store refresh tokens in Keychain or Keystore through a pinned, reviewed wrapper with THIS_DEVICE_ONLY accessibility where suitable.
  • Keep access tokens and PKCE verifiers in memory only.
  • Use system-browser OAuth with PKCE and Universal Links or App Links for redirects.
  • Harden WebViews: restricted origins, no file access, untrusted onMessage handling.
  • Validate every platform channel and native module input; generate channels with Pigeon.
  • Sign OTA updates end to end and hold the key in a KMS.
  • Build Flutter releases with --obfuscate --split-debug-info and keep symbols private, without treating it as a control.
  • Track framework and plugin versions against current stable and upgrade on a schedule.

Standards and references

Related NHI Mgmt Group resources: Mobile App Security Guide · OWASP Mobile Top 10 M9 Insecure Data Storage Guide · OWASP Mobile Top 10 M1 Improper Credential Usage Guide · API Key Management Guide

Free weekly newsletter

Subscribe to the NHI & AI Identity Journal

The latest on NHI and Agentic AI security – articles, research, breaches, news and events every week.

Bonus 33% off our NHI Course when you subscribe.

NHIMG Editorial Note
Researched and written by the NHI Mgmt Group editorial team using AI tools, under the editorial direction of Lalit Choda. Facts are drawn from the sources listed on this page and checked by our automated quality process. Last updated 27 October 2026. Spotted an error? Use our contact page.
NHI Mgmt Group, the independent authority on Non-Human Identity, IAM and Agentic AI security. nhimg.org