Skip to content
Mobile security Reviewed 2026-09-12

Mobile application security

Mobile applications accept data from other apps, deep links, network responses, WebViews, and local storage. Device ownership does not make these inputs trustworthy. Review each boundary against the app's actual data and permissions: a public launcher activity has a different purpose from an internal account-management screen.

This guide covers source and configuration review. Inspecting Kotlin, Swift, Dart, or a manifest is distinct from examining the final APK/IPA, signing, runtime behavior, and release packaging. A source check alone cannot establish complete mobile application security.

Android: expose only intended components

An internal activity should not be exported merely because another screen in the application launches it. Android's android:exported guidance explains how this flag controls access from other applications.

<!-- Unsafe for an activity intended only for this app. -->
<activity android:name=".InternalAccountActivity"
          android:exported="true" />
<!-- Safer for that same internal activity. -->
<activity android:name=".InternalAccountActivity"
          android:exported="false" />

These are fragments inside an Android manifest's <application> element. Public launcher and verified deep-link activities may need to be exported. Validate their incoming URI scheme, host, path, and parameters; re-check authentication and authorization before sensitive actions. Review the merged release manifest, including dependencies, and ensure release builds are not debuggable.

Kotlin: a WebView for untrusted text must not gain native privileges

// Unsafe when pageHtml contains untrusted active content.
webView.settings.javaScriptEnabled = true
webView.addJavascriptInterface(accountBridge, "Account")
webView.loadData(pageHtml, "text/html", "UTF-8")
// Safer when the feature only needs to display plain text.
textView.text = suppliedText

This alternative deliberately uses a native text view, preserving the display requirement without interpreting HTML or exposing a bridge. If a real web feature requires JavaScript, isolate it, restrict navigation, and expose the minimum native capabilities only to trusted content. Android's native-bridge security guidance describes why a JavaScript bridge expands the trust boundary.

iOS: retain transport protections

For an app that only connects to HTTPS services, avoid an unrestricted App Transport Security exception in Info.plist:

<!-- Unsafe broad exception for this HTTPS-only app. -->
<key>NSAppTransportSecurity</key>
<dict><key>NSAllowsArbitraryLoads</key><true/></dict>
<!-- Safer: retain ATS; omitting this exception also preserves its default. -->
<key>NSAppTransportSecurity</key>
<dict><key>NSAllowsArbitraryLoads</key><false/></dict>

Review narrower domain, local-network, media, and WebView exceptions too; the snippet is not a complete audit of every ATS key. Keep standard certificate validation in URLSession. See Apple's ATS configuration reference.

Credentials and platform boundaries

Use platform-protected credential storage such as Apple Keychain services, with accessibility and sharing chosen for the feature. Do not store session tokens in ordinary preferences, source code, public files, or logs. On Android, choose suitable Keystore-backed key handling and protected app storage; a Keystore key and an encrypted file have separate lifecycle and backup requirements.

Flutter applications inherit both Dart and native-platform risks. Do not ship a certificate callback that accepts every certificate, and treat platform-channel messages as inputs requiring validation. Keep long-lived service credentials on the server: secrets distributed inside an app can be extracted.

Regression test

Use local/emulator fixtures and a release build: an ordinary companion test app must not launch the internal activity; the legitimate launcher/deep-link flow must still work. Display harmless markup-looking text and confirm it remains text. Verify HTTPS succeeds and a local invalid-certificate endpoint fails without a validation override. Check the final manifest/plist and verify logout removes or invalidates the intended credentials. Do not infer packaged-app results solely from source snippets.

Related: TLS verification, hardcoded secrets, object authorization, and language guides.

References: CWE-926 — improper export of Android application components, CWE-319 — cleartext transmission of sensitive information, and CWE-312 — cleartext storage of sensitive information.