Skip to main content
Blog

Mobile

Android: allowBackup, cleartext traffic and network security config

Three AndroidManifest settings often turn into mobile expertise findings. How to configure backups, unencrypted traffic and certificate trust correctly.

· 7 min read

Photo: 嘉傑 & Google, CC BY 3.0 · Wikimedia Commons

Many of an Android app's security properties are defined not in code but in AndroidManifest.xml and a few XML files. They look minor, yet in a mobile expertise they often come up as separate findings. While preparing the Android app of our own product for expertise, we revisited three settings: backups (allowBackup), unencrypted HTTP traffic and the network security configuration.

These settings map to the MASVS-STORAGE and MASVS-NETWORK sections of OWASP MASVS.

1. allowBackup: keep data out of backups

By default, Android may back up an app's data (SharedPreferences, databases, internal files) — to the cloud or to a computer via adb backup. If the app stores tokens, sessions or personal data, they leave the device and can be restored on another one.

For an app that handles financial or personal data, the safest option is to disable backups:

<application
    android:allowBackup="false"
    android:fullBackupContent="false"
    android:dataExtractionRules="@xml/data_extraction_rules"
    ... >

On Android 12 (API 31) and above, dataExtractionRules is taken into account alongside allowBackup. So create res/xml/data_extraction_rules.xml and exclude data from both cloud backup and device-to-device transfer:

<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
    <cloud-backup>
        <exclude domain="root" path="." />
        <exclude domain="file" path="." />
        <exclude domain="database" path="." />
        <exclude domain="sharedpref" path="." />
        <exclude domain="external" path="." />
    </cloud-backup>
    <device-transfer>
        <exclude domain="root" path="." />
        <exclude domain="file" path="." />
        <exclude domain="database" path="." />
        <exclude domain="sharedpref" path="." />
        <exclude domain="external" path="." />
    </device-transfer>
</data-extraction-rules>

Disabling backups is only the first step. Our expertise report had a separate high-severity finding: sensitive data was stored unencrypted in internal storage. We closed it by moving to flutter_secure_storage (Keystore-based encryption on Android). In other words, even with backups off, data on the device still has to be encrypted.

2. Cleartext traffic: HTTPS only

Since Android 9 (API 28), unencrypted HTTP traffic is blocked by default when targetSdkVersion is 28 or higher. In practice, though, it often gets switched back on: usesCleartextTraffic="true" is added during development to reach a local server and then forgotten in the release.

State it explicitly in the manifest:

<application
    android:usesCleartextTraffic="false"
    android:networkSecurityConfig="@xml/network_security_config"
    ... >

Check the code too. Our report had a low-severity "insecure (http) links" finding: a few addresses starting with http:// were left in the code. Even if they are unused, static analysis finds them. Search the whole project:

grep -rn "http://" lib/ android/app/src/main/ --include=*.dart --include=*.kt --include=*.java --include=*.xml

XML namespaces (http://schemas.android.com/...) are normal — do not confuse them with network addresses.

3. Network security config: whom we trust

network_security_config.xml defines which certificates the app trusts and which rules apply to which domains. In our case, "insecure certificate validation" was a high-severity finding. To close it, we used this configuration:

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="false">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>

    <domain-config cleartextTrafficPermitted="false">
        <domain includeSubdomains="true">api.example.uz</domain>
        <pin-set expiration="2027-06-30">
            <pin digest="SHA-256">BASE64_ROOT_CA_PIN_1=</pin>
            <pin digest="SHA-256">BASE64_ROOT_CA_PIN_2=</pin>
        </pin-set>
    </domain-config>
</network-security-config>

What matters here:

  • system certificates only. Adding <certificates src="user" /> makes the app trust any certificate the user installs — which makes intercepting traffic through a proxy easier.
  • Pin-set. We pinned specific root CAs rather than the server certificate. That way, the app does not need a new release every time the server certificate is renewed. Use at least two pins (primary and backup).
  • expiration. After this date, pin checking switches off and the app keeps working. This prevents old, un-updated versions from being "locked out". Pick a date that fits your release plan.
  • debug-overrides. If you need a proxy during development, allow it only inside <debug-overrides> — it has no effect in a release build.

A Flutter app has one subtlety: Dart's HTTP client does not fully use Android's network configuration. So we implemented pinning in two layers: pin-set on Android, and in Dart a SecurityContext loaded with trusted root certificates only.

Two more small but useful checks

  • android:debuggable — make sure the manifest does not hard-code true. A release build sets it to false automatically, but a hand-written value can break that.
  • android:exported — set it explicitly for every activity, service and receiver. Components that external apps do not need to call should be exported="false".

How to test the settings in practice

Writing the configuration is not enough — confirm that it works. Install a user certificate on a test device and route traffic through a proxy: the release build should refuse the connection. Then check that the app's data cannot be extracted via adb backup or a device backup. The expertise runs the same kind of practical tests, so it helps to know the outcome in advance.

Short checklist

  1. allowBackup="false" and data_extraction_rules.xml are configured.
  2. Sensitive data is in Keystore-backed encrypted storage.
  3. usesCleartextTraffic="false", and no http:// addresses remain in the code.
  4. network_security_config.xml: system certificates only, with pin-set and expiration.
  5. Debug settings live only inside debug-overrides.
  6. debuggable and exported values have been checked.

Security scan

Want your manifest and network settings checked before the expertise? Send your APK for a security scan. The Claude AI agent reviews the configuration, and our specialist confirms the results and points out exactly which lines to change.