Información sobre el estado de seguridad del dispositivo

La seguridad de tu app depende de la seguridad del SO subyacente. Para mitigar el riesgo, tu app debe restringir las operaciones sensibles a la seguridad con una evaluación en tiempo real de la postura de seguridad del dispositivo, que determine si el dispositivo, en especial sus componentes principales del sistema, está completamente actualizado y protegido contra las vulnerabilidades publicadas en el Boletín de seguridad de Android. La visibilidad del estado de seguridad del dispositivo te ayuda a establecer medidas de protección antes de que tu app ejecute operaciones de alto riesgo.

AndroidX Security State es una biblioteca de Jetpack que proporciona acceso unificado al estado de seguridad de un dispositivo con Android. Combina las APIs del AOSP y los feeds públicos de vulnerabilidades de Android para proporcionar un estado de seguridad del dispositivo integral, preciso y práctico que va más allá del nivel de parche de seguridad (SPL).

Descripción general de la arquitectura

En el siguiente diagrama, se ilustra cómo la biblioteca de AndroidX Security State unifica las propiedades de la plataforma integrado en el dispositivo, los proveedores de actualizaciones de comunicación entre procesos (IPC) y los feeds de vulnerabilidades públicas en un conjunto único y cohesivo de APIs.

La biblioteca de AndroidX Security State unifica las propiedades de la plataforma integradas en el dispositivo, los proveedores de actualizaciones de IPC y los feeds de vulnerabilidades públicas en un conjunto de APIs unificadas.

Estados y componentes de los parches de seguridad

La principal ventaja de usar la biblioteca de Security State es su capacidad de proporcionar el nivel de parche de seguridad (SPL) a un nivel granular y de componentes. Con el paso de los años, Android introdujo más módulos en las actualizaciones del sistema de Google Play (Project Mainline), que se actualizan con Google Play de forma independiente de las actualizaciones inalámbricas (OTA) del sistema estándar, lo que proporciona correcciones de seguridad críticas con una cadencia diferente.

Del mismo modo, si bien el nivel de parche de seguridad suele exigir la versión mínima requerida de la imagen genérica del kernel (GKI), las actualizaciones del kernel pueden adelantarse al cronograma del sistema. Es posible que el sistema no capture por completo estas correcciones específicas del kernel con la cadena de SPL principal.

Para mejorar la transparencia, la biblioteca de estado de seguridad proporciona un nivel de parche efectivo para los tres componentes de forma individual:

  • Sistema (COMPONENT_SYSTEM): Representa el estado de seguridad estándar del SO y del sistema derivado del campo estándar de nivel de parche de seguridad.
  • Módulos de Mainline del sistema (COMPONENT_SYSTEM_MODULES): Representa el estado de seguridad de los componentes modulares del sistema (actualizaciones del sistema de Google Play), que se deriva de versiones de lanzamiento de módulos específicos.
  • Kernel (COMPONENT_KERNEL): Representa el estado de seguridad del kernel del dispositivo, que se deriva directamente de la cadena de versión del kernel.

Según el componente, la biblioteca proporciona hasta 3 dimensiones de información a nivel de parche:

  • SPL del dispositivo (DSPL): Es el nivel de parche actual de los componentes individuales que se ejecutan en el dispositivo, consultado de forma síncrona sin solicitudes de red. El sistema y Mainline informan las fechas del calendario de lanzamiento (DateBasedSecurityPatchLevel), mientras que el kernel informa su versión de lanzamiento (VersionedSecurityPatchLevel, por ejemplo, 5.15.148).
  • SPL publicado (PSPL): Es la versión de referencia que se publica oficialmente en el Boletín de seguridad de Android y en los informes de la base de datos de vulnerabilidades de código abierto (OSV). Tu app puede evaluar el cumplimiento comparando las fechas o las cadenas de versión que proporciona Device SPL.
  • SPL disponible (ASPL): Indica si un dispositivo tiene actualizaciones pendientes disponibles de los proveedores de actualizaciones, consultadas de forma asíncrona con IPC. La función de actualización del sistema de Google Play de Android proporciona disponibilidad para los módulos de Mainline, y los clientes de OTA de OEM proporcionan disponibilidad para las actualizaciones del sistema. Debido a que las actualizaciones del kernel se incluyen en la imagen del sistema operativo, la disponibilidad de las actualizaciones del kernel se evalúa con COMPONENT_SYSTEM.

Control de funciones sensibles a la seguridad

Puedes combinar información del SPL del dispositivo (DSPL), el SPL disponible (ASPL) y el SPL publicado (PSPL) para tomar decisiones contextuales sobre el control de acceso a funciones sensibles a la seguridad. Por ejemplo, puedes comparar el nivel de parche de seguridad de la app (ASPL) con el nivel de parche de seguridad del dispositivo (DSPL) para determinar si hay un parche de seguridad más reciente disponible que el usuario aún no instaló y solicitarle que realice la actualización antes de iniciar operaciones sensibles, como pagos o inscripción de credenciales. También puedes usar areCvesPatched() para verificar si se corrigieron vulnerabilidades específicas de alto riesgo en el dispositivo antes de invocar subsistemas vulnerables, como verificar si hay vulnerabilidades críticas de NFC o Bluetooth antes de autorizar pagos basados en la proximidad o el uso compartido de datos.

Consideraciones sobre la versión de la plataforma

Algunas capacidades del estado de seguridad dependen de la arquitectura de la plataforma subyacente y no están disponibles en versiones anteriores de Android:

  • Android 11 (nivel de API 30) y versiones posteriores: Compatibilidad total con todos los componentes, incluidas las versiones LTS del kernel publicadas en boletines y las consultas de disponibilidad de actualizaciones (ASPL).
  • Android 10 (nivel de API 29): Admite niveles de parche del sistema y del módulo del sistema. Sin embargo, las versiones de kernel publicadas en el boletín no están disponibles porque el seguimiento de la imagen genérica de kernel (GKI) y los objetivos de LTS de kernel en el Boletín de seguridad de Android comenzaron en Android 11. La versión de kernel integrado en el dispositivo aún se puede leer de forma local.
  • Android 9 (nivel de API 28) y versiones anteriores: Además de las limitaciones de Android 10, los componentes del sistema modular (Project Mainline) no existían antes de Android 10. Llamar a getDeviceSecurityPatchLevel(COMPONENT_SYSTEM_MODULES) de forma segura recurre a la fecha de época de Unix de referencia (1970-01-01) cuando los SPL del módulo del sistema no están disponibles.

Información sobre la disponibilidad de actualizaciones y la SPL disponible (ASPL)

Para determinar la postura de seguridad de un dispositivo, es necesario saber si hay actualizaciones de seguridad pendientes disponibles para los componentes individuales. Los dispositivos Android reciben actualizaciones de seguridad a través de varios mecanismos de entrega distintos, como las actualizaciones inalámbricas (OTA) del sistema y las actualizaciones del sistema de Google Play para los componentes modulares.

Antes de que tu app pueda recuperar el ASPL de un componente determinado, el proveedor de actualizaciones correspondiente debe publicar esa información. Para coordinar estas diferentes fuentes de actualización, las aplicaciones cliente usan la biblioteca de AndroidX Security State para consultar el estado de seguridad, mientras que los clientes de actualización integrados en el dispositivo usan la biblioteca complementaria de AndroidX Security State Provider para publicar su nivel de parche de seguridad disponible (ASPL).

Google proporciona información de actualización para los módulos de Mainline a todos los dispositivos Android con GMS, así como información de OTA del sistema para los dispositivos que usan el cliente de OTA de Google (GOTA).

En el siguiente diagrama, se ilustra cómo las aplicaciones cliente usan la biblioteca de Security State de AndroidX para consultar la postura de seguridad en las actualizaciones del sistema de Google Play y las actualizaciones OTA del sistema:

Las aplicaciones cliente usan la biblioteca de AndroidX Security State para consultar la postura de seguridad en las actualizaciones del sistema de Google Play y las actualizaciones OTA del sistema.

Cómo agregar dependencias

Para agregar una dependencia en AndroidX Security State, debes incluir el repositorio de Maven de Google en tu proyecto. Agrega la dependencia al archivo build.gradle.kts o build.gradle de tu app:

Kotlin

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

Groovy

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

Matriz de permisos declarados

APIs de destino Permisos necesarios en AndroidManifest.xml Notas operativas
getDeviceSecurityPatchLevel() Ninguno Lee de forma síncrona las propiedades nativas del sistema locales y los metadatos del paquete.
fetchAvailableSecurityPatchLevel()
queryAllAvailableUpdates()
Ninguno Se comunica a través de la IPC integrado en el dispositivo. La biblioteca solo consulta componentes de confianza en el dispositivo (lo que requiere que los proveedores de actualizaciones tengan el permiso privilegiado READ_PRIVILEGED_PHONE_STATE), por lo que tu app puede confiar en la autenticidad de la información que recibe.
createVulnerabilityReportUrl()
loadVulnerabilityReport()
getPublishedSecurityPatchLevel()
areCvesPatched()
isDeviceFullyUpdated()
android.permission.INTERNET Se requiere para recuperar informes de OSV públicos. Una vez que se cargan en la memoria con loadVulnerabilityReport(), los métodos de consulta se ejecutan de forma local sin acceso a la red.

Cómo inicializar la biblioteca

Inicializa SecurityPatchState con un objeto Context de Android:

Kotlin

import androidx.security.state.SecurityPatchState

val securityPatchState = SecurityPatchState(context)

Java

import androidx.security.state.SecurityPatchState;

SecurityPatchState securityPatchState = new SecurityPatchState(context);

La inicialización solo con context proporciona acceso inmediato a los niveles de parches del dispositivo sin conexión y sin dependencias de red. (Las consultas de disponibilidad de actualización se comunican de forma asíncrona con los proveedores integrados en el dispositivo).

Si tu app evalúa el cumplimiento de CVE y ya cargó un informe de vulnerabilidades de OSV en la memoria, los llamadores de Kotlin pueden pasar la cadena JSON directamente al constructor en Kotlin:

val securityPatchState = SecurityPatchState(context, vulnerabilityReportJsonString = jsonString)

En Java, o cuando se cargan informes de forma asíncrona después del inicio, inicializa con el contexto y llama a securityPatchState.loadVulnerabilityReport(jsonString).

Cómo verificar los niveles de parches del dispositivo

Consulta los niveles de parche integrados en el dispositivo de forma síncrona:

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
}

Cómo verificar si hay actualizaciones del sistema pendientes

Las apps pueden evaluar la disponibilidad de actualizaciones pendientes comunicándose de forma asíncrona con proveedores de actualizaciones de confianza integrados en el dispositivo, que suelen ser las actualizaciones del sistema de Google Play y los clientes OTA del OEM:

  • fetchAvailableSecurityPatchLevel(): Devuelve el SPL disponible efectivo para un componente especificado (recurre al SPL del dispositivo actual si no hay una actualización más reciente disponible) que se puede usar para comparar con el SPL del dispositivo y el SPL publicado.
  • queryAllAvailableUpdates(): Descubre todos los proveedores de actualizaciones de confianza en el dispositivo y devuelve UpdateCheckResult detallados junto con metadatos sobre la fuente y la actualización de los datos.

Ejemplo 1: Solicita a los usuarios que realicen las actualizaciones pendientes

Usa fetchAvailableSecurityPatchLevel() para solicitar a los usuarios que realicen la actualización cuando haya actualizaciones de seguridad disponibles (por ejemplo, iniciando 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

En Java, los métodos asíncronos devuelven un ListenableFuture y enrutan las devoluciones de llamada con 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));

Ejemplo 2: Inspecciona el estado detallado del proveedor

Usa queryAllAvailableUpdates() para inspeccionar proveedores, marcas de tiempo y registros de UpdateInfo individuales:

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

Verifica la resolución de vulnerabilidades y comprueba los CVE

Para evaluar las mitigaciones de CVE, verificar el cumplimiento general de las actualizaciones o inspeccionar los niveles de parche de seguridad publicados (PSPL), tu app primero debe obtener y cargar un informe de vulnerabilidades de OSV, como se detalla en la referencia de la API de 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);

Carga informes de vulnerabilidades

  1. Actualización de WorkManager y almacenamiento en caché local: Debido a que los boletines de seguridad de Android se publican mensualmente, almacena en caché el archivo JSON descargado en el almacenamiento interno (context.filesDir) y programa una tarea periódica en segundo plano con WorkManager (preferentemente cada 24 a 48 horas) para actualizar el informe, leyendo desde la caché local de forma síncrona en el inicio de la app.
  2. Subprocesos en segundo plano: Invoca loadVulnerabilityReport() en un subproceso en segundo plano (Dispatchers.IO en Kotlin o un Executor en Java) para evitar bloquear el subproceso de IU principal mientras se analizan los registros de CVE.
  3. Evaluación del LTS del kernel: La llamada a getPatchedCves(COMPONENT_KERNEL, ...) arroja un IllegalArgumentException, y areCvesPatched() no evalúa las CVE del kernel. En cambio, evalúa la seguridad del kernel comparando la versión del kernel del dispositivo con los objetivos de LTS del kernel común de Android que devuelve getPublishedSecurityPatchLevel(COMPONENT_KERNEL) y que coinciden con su rama principal y secundaria (p.ej., comparando un kernel 5.15.140 con el objetivo 5.15.159).

Recursos adicionales

Para obtener más información sobre el estado de seguridad del dispositivo, consulta los siguientes recursos:

Documentación

Referencia de la API