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

Безопасность приложения зависит от ОС, на которой оно работает. Чтобы снизить риск, приложение должно выполнять операции, связанные с безопасностью, только после оценки конфигурации безопасности устройства в реальном времени. При этом необходимо определить, полностью ли обновлено устройство, особенно его основные системные компоненты, и защищено ли оно от уязвимостей, опубликованных в бюллетене по безопасности Android. Информация о состоянии безопасности устройства помогает вам установить меры защиты до того, как приложение выполнит операции с высоким риском.

AndroidX Security State – это библиотека Jetpack, которая предоставляет унифицированный доступ к состоянию безопасности устройства Android. Он сочетает в себе API AOSP и общедоступные каналы уязвимостей Android, чтобы предоставлять полную, точную и полезную информацию о статусе безопасности устройства, а не только об уровне исправления системы безопасности.

Обзор архитектуры

На схеме ниже показано, как библиотека AndroidX Security State объединяет свойства платформы на устройстве, поставщиков обновлений межпроцессного взаимодействия (IPC) и общедоступные фиды уязвимостей в единый набор API.

Библиотека AndroidX Security State объединяет свойства платформы на устройстве, поставщиков обновлений IPC и общедоступные каналы уязвимостей в набор унифицированных API.

Состояния и компоненты исправлений системы безопасности

Основное преимущество библиотеки Security State – возможность получать информацию об уровне исправлений безопасности (SPL) на уровне отдельных компонентов. С годами в Android появилось больше модулей для обновлений системы через Google Play (проект Mainline), которые обновляются через Google Play независимо от стандартных беспроводных обновлений системы. Это позволяет выпускать важные исправления безопасности с другой периодичностью.

Аналогично, хотя уровень исправления системы безопасности обычно определяет минимальную требуемую версию обобщенного образа ядра (GKI), обновления ядра могут выполняться раньше, чем запланировано. Эти исправления, относящиеся к ядру, могут не быть полностью отражены в основной строке SPL системы.

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

  • Система (COMPONENT_SYSTEM).Стандартный статус ОС и системы безопасности , полученный на основе стандартного поля уровня исправления системы безопасности.
  • Системные модули Mainline (COMPONENT_SYSTEM_MODULES). Статус безопасности модульных системных компонентов (обновлений системы через Google Play), полученный на основе версий определенных модулей.
  • Ядро (COMPONENT_KERNEL). Показывает статус безопасности ядра устройства, полученный непосредственно из строки версии ядра.

В зависимости от компонента библиотека предоставляет до трех параметров информации на уровне исправлений:

  • Уровень исправлений устройства (DSPL). Текущий уровень исправлений отдельных компонентов, работающих на устройстве. Запрос выполняется синхронно без сетевых запросов. В отчетах System и Mainline указываются календарные даты (DateBasedSecurityPatchLevel), а в отчете Kernel – конечная версия выпуска (например, VersionedSecurityPatchLevel, 5.15.148).
  • Опубликованный уровень исправления системы безопасности (PSPL). Базовый уровень, официально опубликованный в бюллетене по безопасности Android и отчетах базы данных уязвимостей с открытым исходным кодом (OSV). Приложение может оценивать соответствие требованиям, сравнивая даты или строки версий, предоставленные Device SPL.
  • Доступный уровень исправлений безопасности (ASPL). Показывает, есть ли на устройстве ожидающие обновления, доступные от поставщиков обновлений, которые запрашиваются асинхронно с помощью IPC. Функция обновления системы через Google Play обеспечивает доступность модулей Mainline, а клиенты OTA от OEM-производителей – доступность системных обновлений. Поскольку обновления ядра входят в образ системы, их доступность оценивается с помощью COMPONENT_SYSTEM.

Как ограничить доступ к функциям, связанным с безопасностью

Вы можете объединить информацию из Device SPL (DSPL), Available SPL (ASPL) и Published SPL (PSPL), чтобы принимать решения о доступе к функциям, связанным с безопасностью. Например, вы можете сравнить DSPL и ASPL, чтобы определить, доступно ли более новое исправление безопасности, которое пользователь ещё не установил, и предложить ему обновить систему перед выполнением конфиденциальных операций, таких как платежи или регистрация учетных данных. Вы также можете использовать areCvesPatched(), чтобы проверить, устранены ли на устройстве определенные уязвимости с высокой степенью риска, прежде чем вызывать уязвимые подсистемы, например проверить наличие критических уязвимостей NFC или Bluetooth перед авторизацией платежей на основе местоположения или передачи данных.

Рекомендации по выбору версии платформы

Некоторые функции, связанные с безопасностью, зависят от архитектуры платформы и недоступны в более ранних версиях Android:

  • Android 11 (уровень API 30) и более поздние версии. Полная поддержка всех компонентов, включая опубликованные в бюллетене версии ядра с долгосрочной поддержкой и запросы о доступности обновлений (ASPL).
  • Android 10 (API уровня 29). Поддерживаются уровни исправлений системы и системного модуля, однако опубликованные в бюллетене версии ядра недоступны, поскольку отслеживание общего образа ядра (GKI) и целевых версий ядра с долгосрочной поддержкой в бюллетене по безопасности Android началось в Android 11. Версию ядра на устройстве можно прочитать локально.
  • Android 9 (уровень API 28) и более ранние версии. Помимо ограничений, действующих в Android 10, следует учитывать, что до Android 10 не существовало модульных системных компонентов (проект Mainline). Если SPL-файлы системного модуля недоступны, при безопасном вызове getDeviceSecurityPatchLevel(COMPONENT_SYSTEM_MODULES) будет использоваться базовая дата эпохи Unix (1970-01-01).

Доступность обновлений и доступный уровень исправлений безопасности (ASPL)

Чтобы определить уровень безопасности устройства, нужно знать, доступны ли для его компонентов ожидающие обновления системы безопасности. Устройства Android получают обновления системы безопасности несколькими способами, например с помощью автоматических обновлений системы и обновлений системы через Google Play для модульных компонентов.

Чтобы приложение могло получить ASPL для определенного компонента, поставщик обновлений должен опубликовать эту информацию. Чтобы координировать работу разных источников обновлений, клиентские приложения используют библиотеку AndroidX Security State для запроса состояния безопасности, а клиенты обновлений на устройстве – библиотеку AndroidX Security State Provider для публикации доступного уровня исправления системы безопасности (ASPL).

Google предоставляет информацию об обновлениях для модулей Mainline всем устройствам Android с сервисами Google Play, а также информацию о системных беспроводных обновлениях для устройств, использующих клиент Google OTA (GOTA).

На диаграмме ниже показано, как клиентские приложения используют библиотеку AndroidX Security State для запроса данных о безопасности в обновлениях системы через Google Play и OTA-обновлениях:

Клиентские приложения используют библиотеку AndroidX Security State, чтобы запрашивать данные о безопасности как в обновлениях системы через Google Play, так и в беспроводных обновлениях.

Добавьте зависимости

Чтобы добавить зависимость от AndroidX Security State, необходимо включить в проект репозиторий Maven Google. Добавьте зависимость в файл build.gradle.kts или build.gradle приложения:

Kotlin

// Kotlin DSL (build.gradle.kts)
dependencies {
    implementation("androidx.security:security-state:1.1.0")
}

Яркий

// Groovy DSL (build.gradle)
dependencies {
    implementation "androidx.security:security-state:1.1.0"
}

Матрица объявленных разрешений

Целевые API Требуемые разрешения в AndroidManifest.xml Оперативные примечания
getDeviceSecurityPatchLevel() Нет Синхронно считывает локальные системные свойства и метаданные пакета.
fetchAvailableSecurityPatchLevel()
queryAllAvailableUpdates()
Нет Обменивается данными с помощью межпроцессного взаимодействия на устройстве. Библиотека запрашивает данные только у доверенных компонентов на устройстве (поставщики обновлений должны иметь привилегированное разрешение READ_PRIVILEGED_PHONE_STATE), поэтому ваше приложение может быть уверено в подлинности полученной информации.
createVulnerabilityReportUrl()
loadVulnerabilityReport()
getPublishedSecurityPatchLevel()
areCvesPatched()
isDeviceFullyUpdated()
android.permission.INTERNET Требуется для получения общедоступных отчетов OSV. После загрузки в память с помощью loadVulnerabilityReport() методы запросов выполняются локально без доступа к сети.

Инициализация библиотеки

Инициализируйте SecurityPatchState с помощью устройства Android Context:

Kotlin

import androidx.security.state.SecurityPatchState

val securityPatchState = SecurityPatchState(context)

Java

import androidx.security.state.SecurityPatchState;

SecurityPatchState securityPatchState = new SecurityPatchState(context);

Инициализация с помощью context обеспечивает немедленный доступ к уровням исправлений офлайн-устройства без зависимостей от сети. (Запросы о доступности обновлений передаются поставщикам на устройстве асинхронно.)

Если ваше приложение проверяет соответствие CVE и уже загрузило в память отчет об уязвимости OSV, вызывающие функции Kotlin могут передавать строку JSON непосредственно конструктору в Kotlin:

val securityPatchState = SecurityPatchState(context, vulnerabilityReportJsonString = jsonString)

В Java или при асинхронной загрузке отчетов после запуска инициализируйте с помощью контекста и вызовите securityPatchState.loadVulnerabilityReport(jsonString).

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

Запрос уровня исправления на устройстве в синхронном режиме:

Kotlin

val deviceSpl = securityPatchState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
val mainlineSpl = securityPatchState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM_MODULES)
val kernelVersion = securityPatchState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_KERNEL)

// Gatekeeping check: compare against a required baseline patch date
val requiredSpl = SecurityPatchState.DateBasedSecurityPatchLevel.fromString("2026-01-01")
if (deviceSpl < requiredSpl) {
    // Restrict access to sensitive features or guide user to update
}

Java

SecurityPatchState.SecurityPatchLevel deviceSpl = securityPatchState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM);
SecurityPatchState.SecurityPatchLevel mainlineSpl = securityPatchState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM_MODULES);
SecurityPatchState.SecurityPatchLevel kernelVersion = securityPatchState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_KERNEL);

// Gatekeeping check: compare against a required baseline patch date
SecurityPatchState.SecurityPatchLevel requiredSpl = SecurityPatchState.DateBasedSecurityPatchLevel.fromString("2026-01-01");
if (deviceSpl.compareTo(requiredSpl) < 0) {
    // Restrict access to sensitive features or guide user to update
}

Проверьте наличие обновлений системы

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

  • fetchAvailableSecurityPatchLevel() – возвращает эффективный доступный патч безопасности для определенного компонента (если нет более нового обновления, используется текущий патч безопасности устройства), который можно сравнить с патчем безопасности устройства и опубликованным патчем безопасности.
  • queryAllAvailableUpdates(): обнаруживает всех доверенных поставщиков обновлений на устройстве и возвращает подробные UpdateCheckResult вместе с метаданными об источнике и актуальности данных.

Пример 1. Предложение пользователям установить ожидающие обновления

Используйте fetchAvailableSecurityPatchLevel(), чтобы показывать пользователям уведомления о доступных обновлениях безопасности (например, запустив Settings.ACTION_SYSTEM_UPDATE_SETTINGS):

Kotlin

import androidx.lifecycle.lifecycleScope
import androidx.security.state.SecurityPatchState
import kotlinx.coroutines.launch

// Pattern A: Prompt users for pending updates (with automatic offline fallback)
lifecycleScope.launch {
    val currentSpl = securityPatchState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
    val availableSpl = securityPatchState.fetchAvailableSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
    if (availableSpl > currentSpl) {
        // Prompt the user to install pending updates in system settings
    }
}

Java

В Java асинхронные методы возвращают ListenableFuture и направляют обратные вызовы с помощью ContextCompat.getMainExecutor():

import androidx.core.content.ContextCompat;
import androidx.security.state.SecurityPatchState;
import androidx.security.state.SecurityPatchState.SecurityPatchLevel;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

// Pattern A: Prompt users for pending updates (with automatic offline fallback)
SecurityPatchLevel currentSpl = securityPatchState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM);
ListenableFuture<SecurityPatchLevel> availableSpl = securityPatchState.fetchAvailableSecurityPatchLevelAsync(
    SecurityPatchState.COMPONENT_SYSTEM,
    SecurityPatchState.UPDATE_INFO_SERVICE_BINDING_TIMEOUT_MS
);

Futures.addCallback(availableSpl, new FutureCallback<SecurityPatchLevel>() {
    @Override
    public void onSuccess(SecurityPatchLevel available) {
        if (available.compareTo(currentSpl) > 0) {
            // Prompt the user to install pending updates in system settings
        }
    }

    @Override
    public void onFailure(Throwable t) {
        // Handle timeout or IPC communication error
    }
}, ContextCompat.getMainExecutor(context));

Пример 2. Проверка подробного статуса поставщика

Используйте queryAllAvailableUpdates(), чтобы проверить отдельных поставщиков, временные метки и записи UpdateInfo:

Kotlin

import androidx.lifecycle.lifecycleScope
import androidx.security.state.SecurityPatchState
import kotlinx.coroutines.launch

// Pattern B: Inspect detailed provider status
lifecycleScope.launch {
    val updateResults = securityPatchState.queryAllAvailableUpdates()
    for (result in updateResults) {
        val provider = result.providerPackageName
        val lastCheck = result.lastCheckTimeMillis
        val updates = result.updates // List<UpdateInfo>
    }
}

Java

import androidx.core.content.ContextCompat;
import androidx.security.state.SecurityPatchState;
import androidx.security.state.UpdateCheckResult;
import androidx.security.state.UpdateInfo;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import java.util.List;

// Pattern B: Inspect detailed provider status
ListenableFuture<List<UpdateCheckResult>> updateResults = securityPatchState.queryAllAvailableUpdatesAsync(
    SecurityPatchState.UPDATE_INFO_SERVICE_BINDING_TIMEOUT_MS
);

Futures.addCallback(updateResults, new FutureCallback<List<UpdateCheckResult>>() {
    @Override
    public void onSuccess(List<UpdateCheckResult> results) {
        for (UpdateCheckResult result : results) {
            String provider = result.getProviderPackageName();
            long lastCheck = result.getLastCheckTimeMillis();
            List<UpdateInfo> updates = result.getUpdates();
        }
    }

    @Override
    public void onFailure(Throwable t) {
        // Handle error
    }
}, ContextCompat.getMainExecutor(context));

Проверка устранения уязвимостей и CVE

Чтобы оценить меры по устранению уязвимостей CVE, проверить соответствие требованиям к обновлениям или изучить опубликованные уровни исправления системы безопасности (PSPL), ваше приложение должно сначала получить и загрузить отчет об уязвимостях OSV, как описано в справочной документации по API для SecurityPatchState.

Kotlin

// Load vulnerability report
val reportUrl = SecurityPatchState.createVulnerabilityReportUrl()
// ... download JSON string from reportUrl ...
securityPatchState.loadVulnerabilityReport(jsonString)

// Check overall update compliance against published bulletin
val isFullyUpdated = securityPatchState.isDeviceFullyUpdated()

val cves = listOf("CVE-2019-9501", "CVE-2020-3699", "CVE-2024-0016")
val isPatched = securityPatchState.areCvesPatched(cves)

// Get a list of all patched CVEs for a specific component and SPL
val deviceSpl = securityPatchState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
val patchedSystemCVEs = securityPatchState.getPatchedCves(SecurityPatchState.COMPONENT_SYSTEM, deviceSpl)

// Inspect published SPL and kernel LTS target versions from the bulletin
val publishedSystemSpl = securityPatchState.getPublishedSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM)
val kernelLtsTargets = securityPatchState.getPublishedSecurityPatchLevel(SecurityPatchState.COMPONENT_KERNEL)

Java

// Load vulnerability report
Uri reportUrl = SecurityPatchState.createVulnerabilityReportUrl();
// ... download JSON string from reportUrl ...
securityPatchState.loadVulnerabilityReport(jsonString);

// Check overall update compliance against published bulletin
boolean isFullyUpdated = securityPatchState.isDeviceFullyUpdated();

List<String> cves = Arrays.asList("CVE-2019-9501", "CVE-2020-3699", "CVE-2024-0016");
boolean isPatched = securityPatchState.areCvesPatched(cves);

// Get a list of all patched CVEs for a specific component and SPL
SecurityPatchState.SecurityPatchLevel deviceSpl = securityPatchState.getDeviceSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM);
Map<SecurityPatchState.Severity, Set<String>> patchedSystemCVEs =
    securityPatchState.getPatchedCves(SecurityPatchState.COMPONENT_SYSTEM, deviceSpl);

// Inspect published SPL and kernel LTS target versions from the bulletin
List<SecurityPatchState.SecurityPatchLevel> publishedSystemSpl =
    securityPatchState.getPublishedSecurityPatchLevel(SecurityPatchState.COMPONENT_SYSTEM);
List<SecurityPatchState.SecurityPatchLevel> kernelLtsTargets =
    securityPatchState.getPublishedSecurityPatchLevel(SecurityPatchState.COMPONENT_KERNEL);

Как загрузить отчеты об уязвимостях

  1. Локальное кеширование и обновление WorkManager. Поскольку бюллетени по безопасности Android публикуются ежемесячно, кешируйте скачанный JSON во внутренней памяти устройства (context.filesDir) и запланируйте периодическую фоновую задачу с помощью WorkManager (желательно каждые 24–48 часов) для обновления отчета, синхронно считывая данные из локального кеша при запуске приложения.
  2. Фоновые потоки. Вызывайте loadVulnerabilityReport() в фоновом потоке (Dispatchers.IO в Kotlin или Executor в Java), чтобы избежать блокировки основного потока UI при анализе записей CVE.
  3. Оценка ядра LTS. Вызов getPatchedCves(COMPONENT_KERNEL, ...) вызывает ошибку IllegalArgumentException, а areCvesPatched() не оценивает уязвимости ядра CVE. Вместо этого оценивайте безопасность ядра, сравнивая версию ядра устройства с целевыми версиями Android Common Kernel LTS, возвращаемыми getPublishedSecurityPatchLevel(COMPONENT_KERNEL), по основной и дополнительной ветвям (например, сравнивая ядро 5.15.140 с целевой версией 5.15.159).

Дополнительные ресурсы

Дополнительную информацию о состоянии безопасности устройства можно найти в следующих ресурсах:

Документация

Документация по API