Manipulation du presse-papiers sécurisé

Catégorie OWASP : MASVS-CODE : qualité du code

Présentation

Android propose un framework puissant appelé presse-papiers pour copier et coller des données entre des applications. Une implémentation incorrecte de cette fonctionnalité peut exposer les données liées aux utilisateurs à des acteurs ou applications malveillants non autorisés.

Le risque spécifique associé à l'exposition des données du presse-papiers dépend de la nature de l'application et des informations permettant d'identifier personnellement l'utilisateur qu'elle gère. L'impact est particulièrement important pour les applications financières, car elles peuvent exposer des données de paiement ou des applications qui gèrent des codes d'authentification à deux facteurs (2FA).

Les vecteurs d'attaque pouvant être exploités pour exfiltrer les données du presse-papiers varient selon la version d'Android:

  • Les versions d'Android antérieures à Android 10 (niveau d'API 29) permettent aux applications en arrière-plan d'accéder aux informations du presse-papiers de l'application au premier plan, ce qui peut permettre aux acteurs malveillants d'accéder directement à toutes les données copiées.
  • À partir d'Android 12 (niveau d'API 31), chaque fois qu'une application accède aux données du presse-papiers et les colle, un message toast s'affiche pour l'utilisateur, ce qui rend les attaques plus difficiles à passer inaperçues. De plus, pour protéger les informations personnelles, Android prend en charge l'indicateur spécial ClipDescription.EXTRA_IS_SENSITIVE ou android.content.extra.IS_SENSITIVE. Cela permet aux développeurs d'obscurcir visuellement l'aperçu du contenu du presse-papiers dans l'IUG du clavier, ce qui empêche les données copiées d'être affichées visuellement en texte clair et potentiellement volées par des applications malveillantes. Ne pas implémenter l'un des indicateurs mentionnés ci-dessus peut en fait permettre aux pirates informatiques d'extraire des données sensibles copiées dans le presse-papiers par espionnage par épaule ou par le biais d'applications malveillantes qui, lorsqu'elles s'exécutent en arrière-plan, prennent des captures d'écran ou enregistrent des vidéos des activités d'un utilisateur légitime.

Impact

L'exploitation d'une gestion incorrecte du presse-papiers peut entraîner l'exfiltration de données financières ou sensibles liées à l'utilisateur par des acteurs malveillants. Cela peut aider les pirates informatiques à effectuer d'autres actions telles que des campagnes d'hameçonnage ou le vol d'identité.

Stratégies d'atténuation

Signaler des données sensibles

Cette solution permet d'obscurcir visuellement l'aperçu du contenu du presse-papiers dans l'IUG du clavier. Toutes les données sensibles pouvant être copiées, telles que les mots de passe ou les données de carte de crédit, doivent être signalées avec ClipDescription.EXTRA_IS_SENSITIVE ou android.content.extra.IS_SENSITIVE avant d'appeler 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);

Appliquer les dernières versions d'Android

En forçant l'application à s'exécuter sur des versions d'Android supérieures ou égales à Android 10 (API 29), les processus en arrière-plan ne peuvent pas accéder aux données du presse-papiers dans l'application de premier plan.

Pour que l'application ne s'exécute que sur Android 10 (API 29) ou version ultérieure, définissez les valeurs suivantes pour les paramètres de version dans les fichiers de compilation Gradle de votre projet dans 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"
          ...
      }
      ...
    }
    ...

Supprimer le contenu du presse-papiers après un certain délai

Si l'application est destinée à s'exécuter sur des versions d'Android antérieures à Android 10 (niveau d'API 29), toute application en arrière-plan peut accéder aux données du presse-papiers. Pour réduire ce risque, il est utile d'implémenter une fonction qui efface toutes les données copiées dans le presse-papiers après un certain temps. Cette fonction est effectuée automatiquement à partir d'Android 13 (niveau d'API 33). Pour les anciennes versions d'Android, cette suppression peut être effectuée en incluant l'extrait de code suivant dans le code de l'application.

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

Ressources