Цифровые сервисы: практика

Как проверить безопасность обновления Android-приложения перед выкладкой в RuStore

Перед выкладкой обновления в RuStore проверьте не только APK/AAB на вирусы, но и изменения в коде: зависимости, разрешения, сетевые настройки, хранение токенов, работу WebView и SDK аналитики.

Краткий алгоритм проверки безопасности перед релизом

Проверка безопасности Android-обновления перед выкладкой в RuStore должна начинаться не в момент загрузки файла в консоль, а до сборки финального релиза. Иначе разработчик рискует найти проблему уже после подписания APK/AAB, когда исправление требует повторной сборки, регрессионного тестирования и переноса даты публикации.

Практический алгоритм можно разбить на 5 этапов.

1. Зафиксируйте состав обновления

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

  • новый код приложения;
  • новые зависимости Gradle;
  • обновлённые версии SDK;
  • изменения в AndroidManifest.xml;
  • новые разрешения Android;
  • изменения в network_security_config.xml;
  • новые домены API;
  • новая логика авторизации;
  • изменение способа хранения токенов, ключей, пользовательских данных;
  • добавление WebView, deep links, push-уведомлений, платежей или рекламных SDK.

Минимум — сформируйте diff между последним релизным тегом и текущей веткой. Например:

git diff v1.8.3..release/1.9.0

Если в команде используется CI/CD, полезно сохранять отчёт по каждому релизу: номер версии, commit hash, дата сборки, SHA-256 APK/AAB, список зависимостей и результат сканирования.

2. Соберите именно релизную версию

Проверять нужно не debug-сборку, а тот APK или AAB, который будет загружен в RuStore. Debug-сборка часто содержит отличия:

  • включённый android:debuggable="true";
  • тестовые API endpoints;
  • расширенные логи;
  • тестовые ключи;
  • отключённую обфускацию;
  • флаги для внутренней диагностики.

Для релизной проверки используйте сборку с теми же параметрами, что и для публикации:

./gradlew clean assembleRelease

или для Android App Bundle:

./gradlew clean bundleRelease

После сборки зафиксируйте контрольную сумму:

sha256sum app-release.apk

Это поможет убедиться, что проверенный файл совпадает с тем, который отправляется в магазин.

3. Проверьте зависимости и известные уязвимости

На этом этапе задача — найти библиотеки с опубликованными CVE, небезопасные версии SDK и транзитивные зависимости, которые попали в приложение незаметно. Для Android-проекта это особенно важно: рекламные, аналитические, push- и платежные SDK часто тянут дополнительные пакеты.

Используйте минимум один инструмент SCA — Software Composition Analysis. На практике применяют:

  • OWASP Dependency-Check;
  • Snyk;
  • GitHub Dependabot;
  • Gradle Versions Plugin;
  • встроенные проверки в GitLab/GitHub CI;
  • коммерческие SCA-сканеры, если приложение работает с платежами или персональными данными.

Критичный порог: зависимости с CVSS 7.0 и выше не должны попадать в релиз без документированного решения. Уязвимости CVSS 9.0–10.0 лучше блокировать автоматически на уровне CI.

4. Проверьте манифест, разрешения и сетевую конфигурацию

AndroidManifest.xml — одно из главных мест, где появляются проблемы при обновлениях. Разработчик добавил SDK, SDK добавил activity, service, receiver или permission, а команда заметила это только после публикации.

Проверьте:

  • не появился ли android:debuggable="true";
  • не открыт ли лишний activity, service, receiver через android:exported="true";
  • есть ли android:permission у экспортируемых компонентов;
  • не добавлены ли опасные разрешения без бизнес-причины;
  • не разрешён ли cleartext-трафик через android:usesCleartextTraffic="true";
  • не ослаблена ли конфигурация TLS;
  • корректно ли настроены deep links и intent filters;
  • нет ли лишнего доступа к контактам, SMS, геолокации, камере, микрофону, файлам.

Для Android 12 и выше наличие android:exported обязательно для компонентов с intent-filter. Но само наличие атрибута не означает безопасность: опасность возникает, если компонент экспортирован без проверки входных данных и прав доступа.

5. Проведите динамическую проверку на устройстве

Статический анализ не покажет всё. Нужно установить релизную сборку на устройство или эмулятор и проверить фактическое поведение:

  • какие запросы уходят в сеть;
  • передаются ли токены только по HTTPS;
  • нет ли персональных данных в логах;
  • корректно ли приложение работает при подмене сети;
  • не сохраняются ли секреты в SharedPreferences открытым текстом;
  • не остаются ли временные файлы с чувствительными данными;
  • как приложение реагирует на истёкшую сессию;
  • можно ли открыть закрытые экраны через deep link;
  • не падает ли приложение при некорректных intent extras.

Для сетевой проверки применяют Burp Suite, mitmproxy, Charles Proxy. Если включён certificate pinning, тестируйте отдельно: с одной стороны, pinning повышает защиту от MITM, с другой — ошибка в реализации может полностью сломать работу приложения после обновления сертификата на сервере.

Анализ зависимостей и сторонних библиотек на наличие эксплойтов

Зависимости — один из самых частых источников риска при обновлении Android-приложения. Команда может не менять критичный код авторизации, но обновить SDK аналитики, push-уведомлений или рекламы, и в релиз попадёт новая сетевая логика, дополнительные разрешения или уязвимая транзитивная библиотека.

Что проверять в Gradle-зависимостях

Начните с полного дерева зависимостей:

./gradlew app:dependencies

Отдельно проверьте конфигурации:

./gradlew app:dependencies --configuration releaseRuntimeClasspath
./gradlew app:dependencies --configuration releaseCompileClasspath

Важно смотреть именно release-конфигурацию, потому что debug-зависимости могут отличаться.

Проверяйте:

  • прямые зависимости в build.gradle или build.gradle.kts;
  • транзитивные зависимости;
  • версии AndroidX;
  • версии Kotlin, Gradle Plugin, Compose;
  • сетевые библиотеки: OkHttp, Retrofit, Ktor;
  • библиотеки сериализации: Gson, Moshi, kotlinx.serialization;
  • WebView-обвязки;
  • криптографические библиотеки;
  • SDK аналитики, рекламы, push, платежей;
  • библиотеки для работы с файлами и изображениями.

Как оценивать найденные уязвимости

Не каждая найденная CVE означает, что релиз нужно остановить. Решение зависит от того, используется ли уязвимый участок в вашем приложении. Но для публикации в магазине лучше придерживаться жёсткой политики:

Уровень CVSSРискДействие перед релизом
0.1–3.9НизкийЗафиксировать, обновить планово
4.0–6.9СреднийПроверить достижимость уязвимого кода
7.0–8.9ВысокийОбновить зависимость или обосновать исключение
9.0–10.0КритическийБлокировать релиз до исправления

Если SCA-инструмент показывает критическую уязвимость в библиотеке, которая включена транзитивно, сначала выясните, кто её подтянул. Иногда достаточно обновить родительский SDK. В других случаях нужно исключить зависимость вручную через Gradle exclude, но это безопасно только после проверки, что SDK действительно не использует исключённый модуль.

Отдельная проверка SDK

Перед обновлением SDK проверьте:

  • официальный changelog;
  • дату последнего релиза;
  • список новых разрешений;
  • изменения политики обработки данных;
  • минимальную поддерживаемую версию Android;
  • наличие native-библиотек .so;
  • появление новых доменов;
  • требования к ProGuard/R8;
  • наличие известных инцидентов безопасности.

Если SDK не обновлялся 2–3 года, использует устаревшие API или не имеет понятной документации, это риск. Для приложений с персональными данными и платежными сценариями такой SDK лучше не добавлять без отдельного анализа.

Проверка секретов в коде и ресурсах

Перед выкладкой обязательно проверьте, не попали ли в сборку:

  • API-ключи;
  • приватные ключи;
  • OAuth client secret;
  • токены доступа;
  • тестовые логины;
  • адреса внутренних стендов;
  • пароли к Basic Auth;
  • ключи шифрования;
  • конфигурации Firebase или других backend-сервисов с лишними правами.

Инструменты для поиска секретов:

  • Gitleaks;
  • TruffleHog;
  • detect-secrets;
  • встроенные secret scanning в GitHub/GitLab;
  • ручная проверка strings app-release.apk.

Команда:

strings app-release.apk | grep -i "secret\|token\|password\|apikey\|client_secret"

Такой поиск не заменяет полноценный аудит, но быстро выявляет грубые ошибки.

Проверка конфигурации AndroidManifest и прав доступа

AndroidManifest.xml нужно сравнивать с предыдущей опубликованной версией. Самая опасная ситуация — когда новое разрешение или экспортируемый компонент появляется не из вашего кода, а из стороннего SDK через manifest merge.

Как получить итоговый манифест

Android Gradle Plugin формирует merged manifest. Проверяйте именно его, а не только исходный файл:

app/build/intermediates/merged_manifests/release/AndroidManifest.xml

В Android Studio можно открыть вкладку Merged Manifest и посмотреть, какая библиотека добавила конкретный permission, activity или service.

Разрешения, которые требуют особого внимания

Опасными для релиза считаются разрешения, связанные с персональными данными, устройством и внешней средой:

  • READ_CONTACTS;
  • WRITE_CONTACTS;
  • ACCESS_FINE_LOCATION;
  • ACCESS_COARSE_LOCATION;
  • RECORD_AUDIO;
  • CAMERA;
  • READ_SMS;
  • SEND_SMS;
  • READ_PHONE_STATE;
  • READ_MEDIA_IMAGES;
  • READ_MEDIA_VIDEO;
  • READ_MEDIA_AUDIO;
  • MANAGE_EXTERNAL_STORAGE;
  • POST_NOTIFICATIONS;
  • REQUEST_INSTALL_PACKAGES.

Если обновление добавляет такое разрешение, у него должна быть понятная функция в приложении. Например, CAMERA оправдана для сканирования QR-кода, но не для обычного справочника или калькулятора. MANAGE_EXTERNAL_STORAGE почти всегда требует дополнительного обоснования и может вызвать вопросы при модерации.

Exported-компоненты

Проверьте все компоненты с android:exported="true":

  • activity;
  • service;
  • receiver;
  • provider.

Для каждого задайте три вопроса:

1. Должен ли компонент вызываться из других приложений?

2. Проверяет ли он входные параметры?

3. Защищён ли он permission, signature-level permission или внутренней логикой авторизации?

Особенно опасны exported activity с deep links. Если приложение открывает экран заказа, профиля, оплаты или документа по внешней ссылке, нужно проверить, что пользователь авторизован, а идентификаторы не позволяют получить чужие данные.

Cleartext-трафик и TLS

В релизной сборке не должно быть незащищённой передачи чувствительных данных по HTTP. Проверьте:

android:usesCleartextTraffic="false"

Если используется network_security_config.xml, убедитесь, что debug-настройки не попали в release:

<debug-overrides>
<trust-anchors>
<certificates src="user"/>
</trust-anchors>
</debug-overrides>

Для release-сборки доверие пользовательским сертификатам может быть опасным, если приложение работает с токенами, платежами или персональными данными. Минимальный стандарт — TLS 1.2+, корректная проверка сертификата сервера и отсутствие отключения hostname verification.

Логи и отладочные флаги

В обновлении не должно быть:

  • Log.d() с токенами, телефонами, email, паспортными данными, адресами;
  • stack trace с backend-ответами;
  • включённого debug-меню;
  • тестовых endpoint;
  • вывода JWT в консоль;
  • сохранения полного HTTP body в логах;
  • флагов BuildConfig.DEBUG, влияющих на безопасность.

Проверьте код по словам:

Log.
printStackTrace
Timber.d
token
Authorization
Bearer
password

Если используется Timber, убедитесь, что debug-дерево не высаживается в release-сборке.

Таблица критических параметров безопасности для RuStore

Ниже — практическая таблица проверки перед загрузкой обновления в RuStore. Её удобно использовать как release gate: если критичный пункт не пройден, сборку не стоит отправлять на модерацию.

ПараметрКак проверитьНорма для релизаЧто делать при проблеме
SHA-256 сборкиsha256sum app-release.apk или аналогХэш сохранён в релизном отчётеПересобрать и повторить проверки для нового файла
DebuggableMerged Manifest, apktool, jadxandroid:debuggable отсутствует или falseИсправить build type release
Подпись приложенияapksignerИспользуется релизный ключ, схема подписи v2/v3/v4 по требованиям сборкиПереподписать корректным ключом
Новые permissionsСравнение merged manifest с прошлым релизомКаждое новое разрешение обосновано функциейУдалить лишнее или описать пользователю причину
Exported-компонентыMerged Manifest, jadxТолько необходимые компоненты экспортированыПоставить exported=false или добавить permission
Cleartext HTTPManifest и network security configДля чувствительных данных только HTTPSОтключить HTTP, перенести API на TLS
Уязвимые зависимостиOWASP Dependency-Check, Snyk, DependabotНет CVSS 7.0+ без решенияОбновить, исключить или документировать риск
Секреты в APK/AABGitleaks, strings, jadxНет приватных ключей и токеновОтозвать секрет, выпустить новый ключ, пересобрать
ОбфускацияПроверка minifyEnabled, mappingДля коммерческого релиза включён R8/ProGuard, если не ломает SDKВключить и протестировать критичные сценарии
ЛогиРучной grep, Logcat на устройствеНет персональных данных и токеновУдалить логи или маскировать значения
Хранение токеновРевью кода, динамический тестТокены не лежат открытым текстом в обычных prefsИспользовать EncryptedSharedPreferences/Keystore
WebViewРевью настроекJS включён только при необходимости, file access ограниченОтключить лишние флаги, фильтровать URL
Deep linksADB-тесты, ручные сценарииНельзя открыть чужие данные по внешнему intentДобавить авторизацию и валидацию параметров
Push-уведомленияТест уведомленийВ тексте нет секретных данныхПередавать ID события, детали загружать после входа
API endpointsПерехват трафикаНет тестовых стендов и внутренних адресовИсправить конфигурацию release
Персональные данныеПроверка сценариевПередаются только необходимые данныеСократить payload, маскировать поля

Для RuStore особенно важно, чтобы заявленная функциональность соответствовала запрашиваемым разрешениям. Если приложение внезапно добавляет доступ к SMS, контактам или установке APK, а пользовательский сценарий этого не объясняет, обновление может получить замечания на проверке.

Типичные уязвимости при обновлении кода и способы их устранения

Обновление кажется менее рискованным, чем первый релиз, но на практике многие уязвимости появляются именно при доработках: срочно добавили новый экран, подключили SDK, расширили API, включили WebView для промо-раздела.

Небезопасное хранение токенов

Проблема: access token, refresh token или session id сохраняются в обычные SharedPreferences без шифрования.

Риск: при компрометации устройства, резервной копии или отладочной сборке токен можно извлечь.

Что делать:

  • хранить чувствительные данные через Android Keystore;
  • использовать EncryptedSharedPreferences;
  • задавать срок жизни access token;
  • не хранить refresh token без необходимости;
  • очищать токены при logout;
  • запрещать резервное копирование чувствительных данных через android:allowBackup="false" или корректную backup-конфигурацию.

Лишние данные в логах

Проблема: в релиз попадают HTTP-запросы и ответы с телефонами, email, токенами, адресами, ID документов.

Риск: данные оказываются в Logcat, crash reports, аналитике или логах технической поддержки.

Что делать:

  • отключить BODY-logging для release;
  • маскировать токены и персональные поля;
  • разделить debug- и release-конфигурации OkHttp interceptor;
  • проверять Logcat на реальном устройстве после установки релизной сборки.

Ошибки в WebView

Проблема: WebView загружает внешние URL, включает JavaScript и разрешает доступ к файлам.

Риск: XSS, фишинговые страницы, утечка локальных файлов, выполнение нежелательного JavaScript.

Что делать:

  • разрешить загрузку только доменов из белого списка;
  • отключить setAllowFileAccess, если он не нужен;
  • не использовать addJavascriptInterface без строгой необходимости;
  • проверять URL перед открытием;
  • не передавать токены в query-параметрах;
  • открывать внешние ссылки во внешнем браузере, если они не относятся к доверенному домену.

Проблема: обновление добавляет ссылки вида app://order/123, но приложение не проверяет владельца заказа.

Риск: пользователь может открыть чужую сущность, если узнает идентификатор.

Что делать:

  • проверять авторизацию на каждом экране;
  • получать данные только с сервера с проверкой прав;
  • не доверять ID из intent;
  • не выполнять чувствительные действия сразу после открытия ссылки;
  • добавлять подтверждение для платежей, удаления, изменения профиля.

Ошибки в криптографии

Проблема: собственная реализация шифрования, статический ключ в коде, устаревшие алгоритмы.

Риск: данные можно расшифровать или подделать.

Что делать:

  • не хранить ключи в ресурсах приложения;
  • использовать Android Keystore;
  • применять современные режимы шифрования, например AES-GCM;
  • не использовать MD5 и SHA-1 для защиты данных;
  • не реализовывать криптографию вручную без экспертизы.

Небезопасная работа с файлами

Проблема: приложение сохраняет документы, фото, отчёты или кэш с персональными данными в общедоступные директории.

Риск: файлы могут быть прочитаны другими приложениями или пользователем без дополнительной защиты.

Что делать:

  • использовать внутреннее хранилище приложения;
  • шифровать чувствительные файлы;
  • очищать временные файлы после использования;
  • не сохранять персональные данные в кэше WebView без необходимости;
  • корректно настраивать FileProvider и не выдавать широкие URI-разрешения.

Когда автоматизированного сканирования недостаточно

Автоматические сканеры хорошо находят известные уязвимости, секреты, небезопасные флаги и подозрительные паттерны. Но они плохо понимают бизнес-логику приложения. Поэтому есть случаи, когда релиз нельзя считать безопасным только потому, что Dependency-Check, Snyk или статический анализатор показали «зелёный» статус.

Обновление добавляет авторизацию или меняет сессии

Если изменились login, logout, refresh token, биометрия, PIN-код или восстановление доступа, нужен ручной тест:

  • нельзя ли использовать старый refresh token после выхода;
  • сбрасывается ли сессия после смены пароля;
  • не сохраняется ли токен после удаления аккаунта;
  • не открываются ли закрытые экраны через back stack;
  • корректно ли работает блокировка после нескольких неудачных попыток.

Обновление добавляет платежи или подписки

Для платежных сценариев проверьте:

  • нельзя ли подменить сумму на клиенте;
  • не доверяет ли backend данным из приложения без серверной проверки;
  • не логируются ли платёжные токены;
  • нет ли тестовых merchant ID в release;
  • не остаются ли sandbox endpoints.

Даже если платежи проходят через внешний SDK, приложение отвечает за то, какие данные оно передаёт и как обрабатывает результат.

Обновление добавляет работу с персональными данными

Если появились профили, документы, адреса, фото, заявки, история операций или сообщения, автоматической проверки мало. Нужен тест доступа:

  • пользователь A не должен видеть данные пользователя B;
  • direct object reference через ID не должен раскрывать чужие записи;
  • push-уведомления не должны содержать чувствительный текст;
  • локальный кэш должен очищаться после выхода;
  • скриншоты чувствительных экранов при необходимости блокируются через FLAG_SECURE.

Обновление добавляет сторонний SDK

SDK может пройти антивирусную проверку, но всё равно быть нежелательным для вашего приложения. Что может пойти не так:

  • SDK добавит лишние permissions;
  • начнёт отправлять дополнительные события;
  • будет собирать рекламный идентификатор без явной необходимости;
  • увеличит поверхность атаки через broadcast receiver;
  • нарушит работу R8/ProGuard;
  • сломает запуск на старых версиях Android;
  • добавит native-библиотеку с уязвимостью.

Перед релизом проверьте SDK в отдельной тестовой сборке и сравните сетевую активность до и после подключения.

Обновление срочное и исправляет критический баг

Срочный релиз не отменяет проверку безопасности. Минимальный набор перед выкладкой:

  • собрать release APK/AAB;
  • проверить SHA-256;
  • прогнать SCA по зависимостям;
  • сравнить AndroidManifest с прошлой версией;
  • установить сборку на устройство;
  • проверить авторизацию, основной сценарий и сеть;
  • убедиться, что debug-флаги не включены.

Если времени мало, лучше сузить проверку до изменённых участков, но не пропускать её полностью.

Чек-лист перед решением о выкладке обновления

Используйте этот список перед загрузкой файла в RuStore. Если хотя бы один критичный пункт не выполнен, релиз стоит остановить или отправить на дополнительную проверку.

  • Проверяется финальная release-сборка, а не debug APK.
  • SHA-256 APK/AAB сохранён в релизном отчёте.
  • Номер версии, commit hash и дата сборки зафиксированы.
  • Merged Manifest сравнен с предыдущим опубликованным релизом.
  • Новые permissions имеют понятное назначение.
  • Нет android:debuggable="true" в release.
  • Нет необоснованных exported activity, service, receiver, provider.
  • usesCleartextTraffic не разрешает HTTP для чувствительных данных.
  • В release не попали тестовые API endpoints.
  • SCA-проверка не нашла CVSS 7.0+ без решения.
  • Секреты не найдены в коде, ресурсах и собранном APK/AAB.
  • Логи не содержат токены, пароли, телефоны, email и персональные данные.
  • Токены не хранятся открытым текстом.
  • WebView ограничен доверенными доменами.
  • Deep links проверяют авторизацию и права доступа.
  • Push-уведомления не раскрывают чувствительные данные.
  • R8/ProGuard не ломает авторизацию, платежи и сериализацию.
  • Приложение протестировано на актуальной версии Android и минимум на одной старой поддерживаемой версии.
  • Проверена работа после обновления поверх предыдущей версии, а не только чистая установка.
  • Критичные сценарии пройдены: вход, выход, восстановление сессии, основной пользовательский путь.
  • Для новых SDK проверены разрешения, сетевые домены и changelog.
  • Решение о публикации зафиксировано ответственным участником команды.

Практическое правило: если обновление меняет только текст, стили или некритичный UI, достаточно стандартной проверки сборки, манифеста и зависимостей. Если обновление меняет авторизацию, платежи, хранение данных, сетевой слой или SDK, нужен расширенный аудит.

Проверка первоисточников

Где сверить правила и документы

Ссылки помогают быстро перейти от советов в статье к официальным реестрам, правилам или справочным сервисам. Перед оплатой или претензией сохраняйте дату проверки.