Co oznacza stan zabezpieczeń urządzenia

Poziom bezpieczeństwa aplikacji jest równy poziomowi bezpieczeństwa systemu operacyjnego, na którym działa. Aby zmniejszyć ryzyko, aplikacja powinna ograniczać operacje wrażliwe pod względem bezpieczeństwa za pomocą oceny w czasie rzeczywistym stanu bezpieczeństwa urządzenia. Ocena ta powinna określać, czy urządzenie, a zwłaszcza jego podstawowe komponenty systemowe, jest w pełni zaktualizowane i chronione przed lukami w zabezpieczeniach opublikowanymi w biuletynie bezpieczeństwa Androida. Wgląd w stan zabezpieczeń urządzenia pomaga wprowadzać zabezpieczenia, zanim aplikacja wykona operacje wysokiego ryzyka.

AndroidX Security State to biblioteka Jetpack, która zapewnia ujednolicony dostęp do stanu zabezpieczeń urządzenia z Androidem. Łączy interfejsy API AOSP i publiczne kanały informacji o lukach w zabezpieczeniach Androida, aby zapewnić kompleksowy, precyzyjny i przydatny stan zabezpieczeń urządzenia, który wykracza poza poziom aktualizacji zabezpieczeń.

Omówienie architektury

Diagram poniżej pokazuje, jak biblioteka AndroidX Security State ujednolica właściwości platformy na urządzeniu, dostawców aktualizacji komunikacji międzyprocesowej (IPC) i publiczne kanały informacji o lukach w zabezpieczeniach w jeden spójny zestaw interfejsów API.

Biblioteka AndroidX Security State ujednolica właściwości platformy na urządzeniu, dostawców aktualizacji IPC i publiczne kanały informacji o lukach w zabezpieczeniach w zestaw ujednoliconych interfejsów API.

Stany i komponenty poprawek zabezpieczeń

Główną zaletą korzystania z biblioteki Security State jest możliwość podawania poziomu aktualizacji zabezpieczeń na poziomie poszczególnych komponentów. Z biegiem lat Android wprowadził więcej modułów do aktualizacji systemowych Google Play (Project Mainline), które są aktualizowane za pomocą Google Play niezależnie od standardowych aktualizacji systemowych OTA, co zapewnia krytyczne poprawki zabezpieczeń w różnych odstępach czasu.

Podobnie jak poziom aktualizacji zabezpieczeń zwykle określa minimalną wymaganą wersję ogólnego obrazu jądra (GKI), aktualizacje jądra mogą wyprzedzać harmonogram systemu. Te poprawki dotyczące jądra mogą nie być w pełni uwzględnione w głównym ciągu znaków SPL systemu.

Aby zwiększyć przejrzystość, biblioteka stanu zabezpieczeń udostępnia efektywny poziom poprawek dla każdego z 3 komponentów z osobna:

  • System (COMPONENT_SYSTEM): reprezentuje standardowy system operacyjny i stan zabezpieczeń systemu pochodzący ze standardowego pola poziomu aktualizacji zabezpieczeń.
  • Moduły systemowe Mainline (COMPONENT_SYSTEM_MODULES): określają stan zabezpieczeń modułowych komponentów systemu (aktualizacji systemu Google Play) na podstawie konkretnych wersji modułów.
  • Jądro (COMPONENT_KERNEL): reprezentuje stan bezpieczeństwa jądra urządzenia, który jest określany bezpośrednio na podstawie ciągu wersji jądra.

W zależności od komponentu biblioteka udostępnia do 3 wymiarów informacji o poziomie poprawki:

  • Poziom poprawek urządzenia (DSPL): bieżący poziom poprawek poszczególnych komponentów działających na urządzeniu, odpytywany synchronicznie bez wysyłania żądań sieciowych. Daty w kalendarzu raportów dotyczących systemu i głównej linii rozwoju (DateBasedSecurityPatchLevel), a raporty dotyczące jądra zawierają wersję do publikacji (VersionedSecurityPatchLevel, np. 5.15.148).
  • Opublikowana lista SPL (PSPL): oficjalna lista bazowa opublikowana w biuletynie bezpieczeństwa Androida i raportach bazy danych Open Source Vulnerabilities (OSV). Aplikacja może ocenić zgodność, porównując daty lub ciągi znaków wersji podane przez Device SPL.
  • Dostępny SPL (ASPL): wskazuje, czy urządzenie ma oczekujące aktualizacje dostępne od dostawców aktualizacji, o które zapytano asynchronicznie za pomocą IPC. Funkcja aktualizacji systemowej Google Play na Androidzie zapewnia dostępność modułów Mainline, a klienci OTA OEM zapewniają dostępność aktualizacji systemu. Ponieważ aktualizacje jądra są dołączane do obrazu systemu operacyjnego, ich dostępność jest oceniana za pomocą COMPONENT_SYSTEM.

Ograniczanie dostępu do funkcji wymagających szczególnej ochrony

Możesz łączyć informacje z listy SPL urządzenia (DSPL), listy SPL dostępnej (ASPL) i listy SPL opublikowanej (PSPL), aby podejmować kontekstowe decyzje dotyczące blokowania funkcji wrażliwych na bezpieczeństwo. Możesz na przykład porównać DSPL z ASPL, aby sprawdzić, czy jest dostępna nowsza poprawka zabezpieczeń, której użytkownik jeszcze nie zainstalował. W takim przypadku możesz poprosić go o aktualizację przed rozpoczęciem operacji wymagających zachowania szczególnej ostrożności, takich jak płatności czy rejestracja danych logowania. Możesz też używać areCvesPatched(), aby sprawdzić, czy na urządzeniu usunięto konkretne luki w zabezpieczeniach o wysokim ryzyku, zanim wywołasz podatne na ataki podsystemy, np. sprawdzić, czy nie ma krytycznych luk w zabezpieczeniach NFC lub Bluetooth, zanim autoryzujesz płatności zbliżeniowe lub udostępnianie danych.

Uwagi dotyczące wersji platformy

Niektóre funkcje związane ze stanem zabezpieczeń zależą od architektury platformy i są niedostępne w starszych wersjach Androida:

  • Android 11 (poziom interfejsu API 30) i nowszy: pełna obsługa wszystkich komponentów, w tym opublikowanych w biuletynie wersji LTS jądra i zapytań o dostępność aktualizacji (ASPL).
  • Android 10 (poziom interfejsu API 29): obsługuje poziomy poprawek systemu i modułu systemowego, ale wersje jądra opublikowane w biuletynie są niedostępne, ponieważ śledzenie podstawowego obrazu jądra (GKI) i docelowych wersji jądra LTS w biuletynie zabezpieczeń Androida rozpoczęło się w Androidzie 11. Wersję jądra na urządzeniu można nadal odczytywać lokalnie.
  • Android 9 (poziom API 28) i starsze wersje: oprócz ograniczeń Androida 10, modułowe komponenty systemu (Project Mainline) nie istniały przed Androidem 10. Wywołanie getDeviceSecurityPatchLevel(COMPONENT_SYSTEM_MODULES) bezpiecznie wraca do podstawowej daty epoki systemu Unix (1970-01-01), gdy SPL modułu systemowego są niedostępne.

Informacje o dostępności aktualizacji i dostępnym poziomie poprawek zabezpieczeń (ASPL)

Określenie stanu zabezpieczeń urządzenia wymaga sprawdzenia, czy dla poszczególnych komponentów są dostępne oczekujące aktualizacje zabezpieczeń. Urządzenia z Androidem otrzymują aktualizacje zabezpieczeń za pomocą różnych mechanizmów dostarczania, takich jak aktualizacje systemu przesyłane drogą radiową (OTA)aktualizacje systemowe Google Play w przypadku komponentów modułowych.

Zanim aplikacja będzie mogła pobrać ASPL dla danego komponentu, odpowiedni dostawca aktualizacji musi opublikować te informacje. Aby koordynować te różne źródła aktualizacji, aplikacje klienckie używają biblioteki AndroidX Security State do sprawdzania stanu zabezpieczeń, a klienci aktualizacji na urządzeniu używają powiązanej biblioteki AndroidX Security State Provider do publikowania dostępnego poziomu aktualizacji zabezpieczeń (ASPL).

Google udostępnia informacje o aktualizacjach modułów Mainline na wszystkich urządzeniach z Androidem GMS, a także informacje o aktualizacjach systemu OTA na urządzeniach korzystających z klienta Google OTA (GOTA).

Poniższy diagram pokazuje, jak aplikacje klienckie używają biblioteki AndroidX Security State do sprawdzania stanu zabezpieczeń w przypadku aktualizacji systemowych Google Play i aktualizacji systemowych OTA:

Aplikacje klienckie używają biblioteki AndroidX Security State do sprawdzania stanu zabezpieczeń w przypadku aktualizacji systemowych Google Play i aktualizacji systemowych OTA.

Dodawanie zależności

Aby dodać zależność od AndroidX Security State, musisz uwzględnić w projekcie repozytorium Maven Google. Dodaj zależność do pliku build.gradle.kts lub build.gradle aplikacji:

Kotlin

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

Dynamiczny

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

Macierz zadeklarowanych uprawnień

Docelowe interfejsy API Wymagane uprawnienia w AndroidManifest.xml Uwagi operacyjne
getDeviceSecurityPatchLevel() Brak Synchroniczne odczytywanie lokalnych właściwości systemu natywnego i metadanych pakietu.
fetchAvailableSecurityPatchLevel()
queryAllAvailableUpdates()
Brak Komunikuje się za pomocą IPC na urządzeniu. Biblioteka wysyła zapytania tylko do zaufanych komponentów na urządzeniu (wymaga to, aby dostawcy aktualizacji mieli uprawnienie READ_PRIVILEGED_PHONE_STATE), więc aplikacja może mieć pewność, że otrzymywane informacje są autentyczne.
createVulnerabilityReportUrl()
loadVulnerabilityReport()
getPublishedSecurityPatchLevel()
areCvesPatched()
isDeviceFullyUpdated()
android.permission.INTERNET Wymagane do pobierania publicznych raportów OSV. Po załadowaniu do pamięci za pomocą loadVulnerabilityReport() metody wysyłania zapytań są wykonywane lokalnie bez dostępu do sieci.

Inicjowanie biblioteki

Zainicjuj SecurityPatchState na urządzeniu z Androidem Context:

Kotlin

import androidx.security.state.SecurityPatchState

val securityPatchState = SecurityPatchState(context)

Java

import androidx.security.state.SecurityPatchState;

SecurityPatchState securityPatchState = new SecurityPatchState(context);

Inicjowanie tylko za pomocą context zapewnia natychmiastowy dostęp do poziomów poprawek urządzenia offline bez zależności od sieci. (Zapytania o dostępność aktualizacji komunikują się asynchronicznie z dostawcami na urządzeniu).

Jeśli aplikacja ocenia zgodność z CVE i ma już wczytany do pamięci raport o lukach w zabezpieczeniach OSV, wywołujący w Kotlinie mogą przekazać ciąg znaków JSON bezpośrednio do konstruktora w Kotlinie:

val securityPatchState = SecurityPatchState(context, vulnerabilityReportJsonString = jsonString)

W przypadku Javy lub podczas asynchronicznego wczytywania raportów po uruchomieniu zainicjuj kontekst i wywołaj securityPatchState.loadVulnerabilityReport(jsonString).

Sprawdzanie poziomów aktualizacji urządzenia

Synchroniczne wysyłanie zapytań o poziomy aktualizacji na urządzeniu:

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
}

Sprawdzanie dostępności aktualizacji systemu

Aplikacje mogą oceniać dostępność oczekujących aktualizacji, komunikując się asynchronicznie z zaufanymi dostawcami aktualizacji na urządzeniu, najczęściej z aktualizacjami systemowymi Google Play i klientami OTA producentów OEM:

  • fetchAvailableSecurityPatchLevel(): zwraca obowiązujący poziom SPL dla określonego komponentu (w przypadku braku nowszej aktualizacji przywraca bieżący poziom SPL urządzenia), który można wykorzystać do porównania z poziomem SPL urządzenia i opublikowanym poziomem SPL.
  • queryAllAvailableUpdates(): wykrywa wszystkich zaufanych dostawców aktualizacji na urządzeniu i zwraca szczegółowe UpdateCheckResult wraz z metadanymi dotyczącymi źródła i aktualności danych.

Przykład 1. Zachęcanie użytkowników do zastosowania oczekujących aktualizacji

Użyj fetchAvailableSecurityPatchLevel(), aby zachęcać użytkowników do aktualizacji zabezpieczeń (np. uruchamiając 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

W Javie metody asynchroniczne zwracają obiekt ListenableFuture i wywołania zwrotne trasy za pomocą 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));

Przykład 2. Sprawdzanie szczegółowego stanu dostawcy

Użyj queryAllAvailableUpdates(), aby sprawdzić poszczególnych dostawców, sygnatury czasowe i rekordy 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));

Sprawdź rozwiązanie problemu z luką w zabezpieczeniach i luki CVE

Aby ocenić środki zaradcze dotyczące CVE, sprawdzić ogólną zgodność aktualizacji lub przejrzeć opublikowane poziomy aktualizacji zabezpieczeń, aplikacja musi najpierw uzyskać i wczytać raport o lukach w zabezpieczeniach OSV, zgodnie z opisem w dokumentacji interfejsu 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);

Wczytywanie raportów o lukach w zabezpieczeniach

  1. Lokalne buforowanie i odświeżanie za pomocą WorkManagera: ponieważ biuletyny zabezpieczeń Androida są publikowane co miesiąc, pobrany plik JSON należy buforować w pamięci wewnętrznej (context.filesDir) i zaplanować okresowe zadanie w tle za pomocą WorkManager (najlepiej co 24–48 godzin), aby odświeżać raport, odczytując synchronicznie dane z lokalnej pamięci podręcznej przy uruchamianiu aplikacji.
  2. Wątki w tle: wywołuj loadVulnerabilityReport() w wątku w tle (Dispatchers.IO w Kotlinie lub Executor w Javie), aby uniknąć blokowania głównego wątku UI podczas analizowania rekordów CVE.
  3. Ocena jądra LTS: wywołanie funkcji getPatchedCves(COMPONENT_KERNEL, ...) powoduje zgłoszenie wyjątku IllegalArgumentException, a funkcja areCvesPatched() nie ocenia luk w zabezpieczeniach jądra. Zamiast tego oceń bezpieczeństwo jądra, porównując wersję jądra urządzenia z docelowymi wersjami LTS wspólnego jądra Androida zwracanymi przez getPublishedSecurityPatchLevel(COMPONENT_KERNEL), które pasują do jego głównej i pomniejszej gałęzi (np. porównując jądro 5.15.140 z wersją docelową 5.15.159).

Dodatkowe materiały

Więcej informacji o stanie bezpieczeństwa urządzenia znajdziesz w tych materiałach:

Dokumentacja

Dokumentacja API