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 les applications. Une implémentation incorrecte de cette fonctionnalité pourrait exposer les données utilisateur à des applications ou des personnes malveillantes non autorisées.

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 traite. L'impact est particulièrement élevé pour les applications financières, car elles peuvent exposer des données de paiement, ou pour les applications qui gèrent les codes d'authentification à deux facteurs (2FA).

Les vecteurs d'attaque qui pourraient être utilisés pour exfiltrer les données du presse-papiers varient en fonction de 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 des applications au premier plan, ce qui peut permettre à des personnes malveillantes 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 à des données dans le 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, afin de protéger les informations permettant d'identifier personnellement l'utilisateur, Android est compatible avec 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'interface utilisateur graphique du clavier, ce qui empêche les données copiées d'être affichées en texte clair et potentiellement volées par des applications malveillantes. Le fait de ne pas implémenter l'un des indicateurs susmentionnés pourrait en fait permettre aux pirates informatiques d'exfiltrer des données sensibles copiées dans le presse-papiers en espionnant l'utilisateur ou par le biais d'applications malveillantes qui, tout en s'exécutant 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 mauvaise gestion du presse-papiers peut entraîner l'exfiltration de données sensibles ou financières liées à l'utilisateur par des personnes malveillantes. Cela peut aider les pirates informatiques à mener d'autres actions, comme des campagnes d'hameçonnage ou des vols d'identité.

Stratégies d'atténuation

Signaler les données sensibles

Cette solution est utilisée pour masquer visuellement l'aperçu du contenu du presse-papiers dans l'interface utilisateur 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 ultérieures ou égales à Android 10 (API 29), vous empêchez les processus en arrière-plan d'accéder aux données du presse-papiers dans l'application au premier plan.

Pour forcer l'application à ne s'exécuter 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 une période définie

Si l'application est conçue pour 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 une période spécifique. Cette fonction est exécuté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 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