Перейти к основному содержанию
Блог

Мобильные

Android: allowBackup, cleartext-трафик и network security config

Три настройки в AndroidManifest часто становятся находками мобильной экспертизы. Как правильно настроить резервное копирование, незашифрованный трафик и доверие к сертификатам.

· 7 мин. чтения

Фото: 嘉傑 & Google, CC BY 3.0 · Wikimedia Commons

Многие свойства безопасности Android-приложения задаются не в коде, а в AndroidManifest.xml и нескольких XML-файлах. Они выглядят мелочами, но на мобильной экспертизе часто выходят отдельными находками. Готовя Android-приложение нашего собственного продукта к экспертизе, мы пересмотрели три настройки: резервное копирование (allowBackup), незашифрованный HTTP-трафик и конфигурацию сетевой безопасности.

Эти настройки соответствуют разделам MASVS-STORAGE и MASVS-NETWORK стандарта OWASP MASVS.

1. allowBackup: данные не должны уходить в резервную копию

По умолчанию Android может делать резервную копию данных приложения (SharedPreferences, базы данных, внутренние файлы) — в облако или на компьютер через adb backup. Если в приложении хранятся токены, сессии или персональные данные, они покидают устройство и могут быть восстановлены на другом.

Для приложения, работающего с финансовыми или персональными данными, самый безопасный вариант — отключить резервное копирование:

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

На Android 12 (API 31) и выше вместе с allowBackup учитывается и dataExtractionRules. Поэтому создайте файл res/xml/data_extraction_rules.xml и исключите данные как из облачной копии, так и из переноса между устройствами:

<?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>

Отключение резервного копирования — только первый шаг. На экспертизе у нас была отдельная находка высокого уровня: чувствительные данные хранились во внутренней памяти в незашифрованном виде. Мы закрыли её переходом на flutter_secure_storage (на Android — шифрование на основе Keystore). То есть даже при отключённом бэкапе данные на устройстве всё равно должны быть зашифрованы.

2. Cleartext-трафик: только HTTPS

Начиная с Android 9 (API 28), при targetSdkVersion 28 и выше незашифрованный HTTP-трафик по умолчанию блокируется. Но на практике его часто включают обратно: на время разработки добавляют usesCleartextTraffic="true" для подключения к локальному серверу и забывают убрать в release.

Укажите это в манифесте явно:

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

Проверьте и сам код. В нашем отчёте была находка низкого уровня «небезопасные (http) ссылки»: в коде остались несколько адресов, начинающихся с http://. Даже если они не используются, статический анализ их находит. Поищите по всему проекту:

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

Пространства имён XML (http://schemas.android.com/...) — это нормально, не путайте их с сетевыми адресами.

3. Network security config: кому мы доверяем

network_security_config.xml определяет, каким сертификатам доверяет приложение и какие правила действуют для каких доменов. Находка «небезопасная проверка сертификата» была у нас высокого уровня. Чтобы закрыть её, мы применили такую конфигурацию:

<?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>

Что здесь важно:

  • Только system-сертификаты. Если добавить <certificates src="user" />, приложение будет доверять любому сертификату, установленному пользователем, — а это упрощает перехват трафика через прокси.
  • Pin-set. Мы закрепили не сертификат сервера, а конкретные корневые CA. Тогда при каждом обновлении серверного сертификата не нужно выпускать новую версию приложения. Ставьте минимум два пина (основной и резервный).
  • expiration. После этой даты проверка пинов отключается, и приложение продолжает работать. Это защищает старые необновлённые версии от «блокировки». Выбирайте дату в соответствии с планом релизов.
  • debug-overrides. Если для разработки нужен прокси, разрешайте его только внутри <debug-overrides> — в release-сборке это не действует.

В Flutter-приложении есть тонкость: HTTP-клиент Dart не полностью использует сетевую конфигурацию Android. Поэтому мы сделали pinning в два слоя: на Android — pin-set, в Dart — SecurityContext, в который загружены только доверенные корневые сертификаты.

Ещё две небольшие, но полезные проверки

  • android:debuggable — убедитесь, что в манифесте вручную не прописано true. Release-сборка автоматически ставит false, но явно заданное значение может это нарушить.
  • android:exported — укажите явно для каждого activity, service, receiver. Компоненты, которые не должны вызываться внешними приложениями, должны иметь exported="false".

Как проверить настройки на практике

Написать конфигурацию недостаточно — нужно убедиться, что она работает. Установите на тестовое устройство пользовательский сертификат и пропустите трафик через прокси: release-сборка должна отказать в соединении. Затем проверьте, что данные приложения не извлекаются через adb backup или резервную копию устройства. Экспертиза проводит такие же практические проверки, поэтому полезно знать результат заранее.

Короткий чек-лист

  1. Настроены allowBackup="false" и data_extraction_rules.xml.
  2. Чувствительные данные — в зашифрованном хранилище на основе Keystore.
  3. usesCleartextTraffic="false", в коде не осталось адресов http://.
  4. network_security_config.xml: только system-сертификаты, есть pin-set и expiration.
  5. Отладочные настройки — только внутри debug-overrides.
  6. Значения debuggable и exported проверены.

Сканирование

Хотите проверить манифест и сетевые настройки до экспертизы? Отправьте APK на сканирование. ИИ-агент Claude просмотрит конфигурацию, а наш специалист подтвердит результат и точно покажет, какие строки нужно изменить.