Безопасная обработка буфера обмена

Категория OWASP: MASVS-CODE: Качество кода

Обзор

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

Конкретный риск, связанный с утечкой данных из буфера обмена, зависит от характера приложения и обрабатываемой им персональной идентифицирующей информации (PII). Особенно высок риск для финансовых приложений, поскольку они могут раскрывать платежные данные, или для приложений, обрабатывающих коды двухфакторной аутентификации (2FA).

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

  • В версиях Android старше 10 (уровень API 29) фоновые приложения могут получать доступ к информации из буфера обмена приложений, работающих в фоновом режиме, что потенциально позволяет злоумышленникам напрямую получать доступ к любым скопированным данным.
  • Начиная с Android 12 (уровень API 31), каждый раз, когда приложение обращается к данным в буфере обмена и вставляет их, пользователю отображается всплывающее сообщение, что затрудняет незамеченные атаки. Кроме того, для защиты персональных данных Android поддерживает специальный флаг ClipDescription.EXTRA_IS_SENSITIVE или android.content.extra.IS_SENSITIVE . Это позволяет разработчикам визуально скрывать предварительный просмотр содержимого буфера обмена в графическом интерфейсе клавиатуры, предотвращая визуальное отображение скопированных данных в открытом виде и их потенциальную кражу вредоносными приложениями. Отсутствие одного из вышеупомянутых флагов может фактически позволить злоумышленникам похищать конфиденциальные данные, скопированные в буфер обмена, либо путем подглядывания, либо с помощью вредоносных приложений, которые, работая в фоновом режиме, делают снимки экрана или записывают видео действий законного пользователя.

Влияние

Использование уязвимостей в обработке буфера обмена может привести к утечке конфиденциальных или финансовых данных пользователей злоумышленниками. Это может помочь злоумышленникам в проведении дальнейших действий, таких как фишинговые кампании или кража личных данных.

Меры по смягчению последствий

Пометить конфиденциальные данные

Это решение используется для визуального сокрытия предварительного просмотра содержимого буфера обмена в графическом интерфейсе клавиатуры. Любые конфиденциальные данные, которые могут быть скопированы, такие как пароли или данные кредитных карт, должны быть помечены с помощью ClipDescription.EXTRA_IS_SENSITIVE или android.content.extra.IS_SENSITIVE перед вызовом ClipboardManager.setPrimaryClip() .

Котлин

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

Принудительное использование последних версий Android

Требование запуска приложения на версиях Android, отличных от Android 10 (API 29), предотвращает доступ фоновых процессов к данным буфера обмена в приложении, работающем на переднем плане.

Чтобы приложение работало только на Android 10 (API 29) или более поздних версиях, установите следующие значения для параметров версии в файлах сборки Gradle в вашем проекте в Android Studio.

Классный

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"
          ...
      }
      ...
    }
    ...

Котлин

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"
          ...
      }
      ...
    }
    ...

Удалять содержимое буфера обмена по истечении заданного периода времени.

Если приложение предназначено для работы на версиях Android ниже 10 (уровень API 29), любое фоновое приложение может получить доступ к данным буфера обмена. Чтобы снизить этот риск, полезно реализовать функцию, которая очищает все данные, скопированные в буфер обмена, по истечении определенного периода времени. Эта функция автоматически выполняется начиная с Android 13 (уровень API 33) . Для более старых версий Android это удаление можно выполнить, добавив следующий фрагмент кода в приложение.

Котлин

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

Ресурсы