Obsługa bezpiecznego schowka

Kategoria OWASP: MASVS-CODE: Code Quality

Omówienie

Android oferuje zaawansowany framework zwany schowkiem do kopiowania i wklejania danych między aplikacjami. Niewłaściwe wdrożenie tej funkcji może narazić dane użytkowników na nieautoryzowany dostęp ze strony osób lub aplikacji, które mają złe zamiary.

Konkretne ryzyko związane z wyłożeniem danych z bufora komunikacyjnego zależy od charakteru aplikacji i danych osobowych, z którymi ma ona do czynienia. W szczególności dotyczy to aplikacji finansowych, które mogą ujawniać dane do płatności, oraz aplikacji obsługujących kody uwierzytelniania dwuskładnikowego (2FA).

Wektory ataków, które można wykorzystać do wydobycia danych ze schowka, różnią się w zależności od wersji Androida:

  • Wersje Androida starsze niż 10 (poziom interfejsu API 29) umożliwiają aplikacjom działającym w tle dostęp do informacji ze schowka aplikacji na pierwszym planie, co może umożliwić złośliwym podmiotom bezpośredni dostęp do skopiowanych danych.
  • Od Androida 12 (poziom interfejsu API 31) za każdym razem, gdy aplikacja odczytuje dane z bufora i je wklei, użytkownik zobaczy komunikat wyskakujący, co utrudnia przeprowadzenie niezauważalnych ataków. Dodatkowo, aby chronić informacje o osobach, Android obsługuje flagę specjalną ClipDescription.EXTRA_IS_SENSITIVE lub android.content.extra.IS_SENSITIVE. Umożliwia to deweloperom zaciemnianie podglądu treści skopiowanych do schowka w interfejsie graficznym klawiatury, co uniemożliwia wyświetlanie skopiowanych danych w postaci tekstu zwykłego i ich potencjalne kradzież przez złośliwe aplikacje. Niewdrożenie któregoś z wymienionych flag może umożliwić atakującym pozyskiwanie poufnych danych skopiowanych do schowka przez podglądanie przez ramię lub za pomocą złośliwych aplikacji, które podczas działania w tle wykonują zrzuty ekranu lub nagrywają filmy z działalności legalnego użytkownika.

Wpływ

Wykorzystanie nieprawidłowego zarządzania schowka może skutkować pozyskaniem przez osoby o złośliwych zamiarach poufnych danych użytkownika lub informacji o jego finansach. Może to ułatwić atakującym przeprowadzenie dalszych działań, takich jak kampanie phishingowe czy kradzież tożsamości.

Środki zaradcze

Oznaczanie danych wrażliwych

To rozwiązanie służy do zaciemnienia podglądu zawartości schowka w interfejsie graficznym klawiatury. Wszystkie informacje poufne, które można skopiować, takie jak hasła czy dane karty kredytowej, powinny być oznaczone jako ClipDescription.EXTRA_IS_SENSITIVE lub android.content.extra.IS_SENSITIVE przed nawiązaniem połączenia z ClipboardManager.setPrimaryClip().

Kotlin

// If your app is compiled with the API level 33 SDK or higher.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)
    }
}

// If your app is compiled with API level 32 SDK or lower.
clipData.apply {
    description.extras = PersistableBundle().apply {
        putBoolean("android.content.extra.IS_SENSITIVE", true)
    }
}

Java

// If your app is compiled with the API level 33 SDK or higher.
PersistableBundle extras = new PersistableBundle();
extras.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true);
clipData.getDescription().setExtras(extras);

// If your app is compiled with API level 32 SDK or lower.
PersistableBundle extras = new PersistableBundle();
extras.putBoolean("android.content.extra.IS_SENSITIVE", true);
clipData.getDescription().setExtras(extras);

Wymaganie najnowszych wersji Androida

Wymaganie uruchamiania aplikacji w wersjach Androida 10 lub nowszych (API 29) uniemożliwia procesom działającym w tle dostęp do danych schowka w aplikacji na pierwszym planie.

Aby wymusić uruchamianie aplikacji tylko na Androidzie 10 (interfejs API 29) lub nowszym, ustaw te wartości dla ustawień wersji w plikach kompilacji Gradle w projekcie w Android Studio.

Groovy

android {
      namespace 'com.example.testapp'
      compileSdk [SDK_LATEST_VERSION]

      defaultConfig {
          applicationId "com.example.testapp"
          minSdk 29
          targetSdk [SDK_LATEST_VERSION]
          versionCode 1
          versionName "1.0"
          ...
      }
      ...
    }
    ...

Kotlin

android {
      namespace = "com.example.testapp"
      compileSdk = [SDK_LATEST_VERSION]

      defaultConfig {
          applicationId = "com.example.testapp"
          minSdk = 29
          targetSdk = [SDK_LATEST_VERSION]
          versionCode = 1
          versionName = "1.0"
          ...
      }
      ...
    }
    ...

Usuwanie treści ze schowka po określonym czasie

Jeśli aplikacja ma działać na wersjach Androida starszych niż 10 (poziom interfejsu API 29), każda aplikacja działająca w tle może uzyskać dostęp do danych schowka. Aby zmniejszyć to ryzyko, warto wdrożyć funkcję, która po określonym czasie usuwa wszystkie dane skopiowane do schowka. Ta funkcja jest wykonana automatycznie od Androida 13 (poziom API 33). W starszych wersjach Androida można usunąć te pliki, dodając do kodu aplikacji ten fragment kodu.

Kotlin

//The Executor makes this task Asynchronous so that the UI continues being responsive
backgroundExecutor.schedule({
    //Creates a clip object with the content of the Clipboard
    val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
    val clip = clipboard.primaryClip
    //If SDK version is higher or equal to 28, it deletes Clipboard data with clearPrimaryClip()
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
        clipboard.clearPrimaryClip()
    } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
    //If SDK version is lower than 28, it will replace Clipboard content with an empty value
        val newEmptyClip = ClipData.newPlainText("EmptyClipContent", "")
        clipboard.setPrimaryClip(newEmptyClip)
     }
//The delay after which the Clipboard is cleared, measured in seconds
}, 5, TimeUnit.SECONDS)

Java

//The Executor makes this task Asynchronous so that the UI continues being responsive

ScheduledExecutorService backgroundExecutor = Executors.newSingleThreadScheduledExecutor();

backgroundExecutor.schedule(new Runnable() {
    @Override
    public void run() {
        //Creates a clip object with the content of the Clipboard
        ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = clipboard.getPrimaryClip();
        //If SDK version is higher or equal to 28, it deletes Clipboard data with clearPrimaryClip()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            clipboard.clearPrimaryClip();
            //If SDK version is lower than 28, it will replace Clipboard content with an empty value
        } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
            ClipData newEmptyClip = ClipData.newPlainText("EmptyClipContent", "");
            clipboard.setPrimaryClip(newEmptyClip);
        }
    //The delay after which the Clipboard is cleared, measured in seconds
    }, 5, TimeUnit.SECONDS);

Materiały