Понимание состояния безопасности устройства

Безопасность вашего приложения напрямую зависит от безопасности его операционной системы. Для снижения рисков ваше приложение должно проверять безопасность операций в режиме реального времени, оценивая состояние безопасности устройства и определяя, полностью ли обновлено устройство, особенно его основные системные компоненты, и защищено ли оно от уязвимостей, опубликованных в бюллетене безопасности Android . Видимость состояния безопасности устройства помогает установить меры защиты до того, как ваше приложение начнет выполнять операции с высоким риском.

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

Архитектурный обзор

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

The AndroidX Security State library unifies on-device platform properties, IPC update providers, and public vulnerability feeds into a set of unified APIs

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

Главное преимущество использования библиотеки Security State заключается в её способности предоставлять уровень исправлений безопасности (SPL) на детальном, компонентном уровне. За прошедшие годы Android добавил больше модулей в обновления системы Google Play (Project Mainline) , которые обновляются через Google Play независимо от стандартных системных обновлений по беспроводной сети (OTA) , предоставляя критически важные исправления безопасности с другой периодичностью.

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

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

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

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

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

Функции, обеспечивающие безопасность ворот

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

Вопросы, касающиеся версий платформы.

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

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

Разберитесь в доступности обновлений и доступном уровне SPL (ASPL).

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

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

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

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

Client applications use the AndroidX Security State library to query security posture across both Google Play system updates and system OTA updates

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

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

Котлин

// 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 с использованием Context Android:

Котлин

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) .

Проверьте уровни обновлений устройства.

Синхронный запрос уровней обновлений на устройстве:

Котлин

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-клиенты от производителей оборудования:

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

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

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

Котлин

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 :

Котлин

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 .

Котлин

// 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), чтобы избежать блокировки основного потока пользовательского интерфейса во время анализа записей CVE.
  3. Оценка безопасности ядра LTS: вызов функции getPatchedCves(COMPONENT_KERNEL, ...) вызывает исключение IllegalArgumentException , а areCvesPatched() не оценивает уязвимости CVE ядра. Вместо этого оценивается безопасность ядра путем сравнения версии ядра устройства с целевыми версиями Android Common Kernel LTS, возвращаемыми функцией getPublishedSecurityPatchLevel(COMPONENT_KERNEL) соответствующими его основной и дополнительной веткам (например, сравнение ядра 5.15.140 с целевой версией 5.15.159 ).

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

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

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

Справочник API