Как проверить безопасность обновления 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 или аналог | Хэш сохранён в релизном отчёте | Пересобрать и повторить проверки для нового файла |
| Debuggable | Merged Manifest, apktool, jadx | android:debuggable отсутствует или false | Исправить build type release |
| Подпись приложения | apksigner | Используется релизный ключ, схема подписи v2/v3/v4 по требованиям сборки | Переподписать корректным ключом |
| Новые permissions | Сравнение merged manifest с прошлым релизом | Каждое новое разрешение обосновано функцией | Удалить лишнее или описать пользователю причину |
| Exported-компоненты | Merged Manifest, jadx | Только необходимые компоненты экспортированы | Поставить exported=false или добавить permission |
| Cleartext HTTP | Manifest и network security config | Для чувствительных данных только HTTPS | Отключить HTTP, перенести API на TLS |
| Уязвимые зависимости | OWASP Dependency-Check, Snyk, Dependabot | Нет CVSS 7.0+ без решения | Обновить, исключить или документировать риск |
| Секреты в APK/AAB | Gitleaks, strings, jadx | Нет приватных ключей и токенов | Отозвать секрет, выпустить новый ключ, пересобрать |
| Обфускация | Проверка minifyEnabled, mapping | Для коммерческого релиза включён R8/ProGuard, если не ломает SDK | Включить и протестировать критичные сценарии |
| Логи | Ручной grep, Logcat на устройстве | Нет персональных данных и токенов | Удалить логи или маскировать значения |
| Хранение токенов | Ревью кода, динамический тест | Токены не лежат открытым текстом в обычных prefs | Использовать EncryptedSharedPreferences/Keystore |
| WebView | Ревью настроек | JS включён только при необходимости, file access ограничен | Отключить лишние флаги, фильтровать URL |
| Deep links | ADB-тесты, ручные сценарии | Нельзя открыть чужие данные по внешнему 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-параметрах;
- открывать внешние ссылки во внешнем браузере, если они не относятся к доверенному домену.
Небезопасные deep links
Проблема: обновление добавляет ссылки вида 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, нужен расширенный аудит.
Проверка первоисточников
Где сверить правила и документы
Ссылки помогают быстро перейти от советов в статье к официальным реестрам, правилам или справочным сервисам. Перед оплатой или претензией сохраняйте дату проверки.
