Manejo seguro del portapapeles
Organiza tus páginas con colecciones
Guarda y categoriza el contenido según tus preferencias.
Categoría de OWASP: MASVS-CODE: Calidad de código
Descripción general
Android ofrece un framework potente conocido como portapapeles para copiar y pegar datos entre aplicaciones. Una implementación incorrecta de esta función podría exponer los datos relacionados con el usuario a aplicaciones o personas malintencionadas no autorizadas.
El riesgo específico asociado con la exposición de los datos del portapapeles depende de la
naturaleza de la aplicación y de la información de identificación personal (PII) que esta
administra. El impacto es especialmente alto para las aplicaciones financieras, ya que pueden exponer datos de pago, o bien apps que controlan códigos de autenticación de dos factores (2FA).
Los vectores de ataque que se podrían aprovechar para extraer datos del portapapeles varían según la versión de Android:
- Las versiones de Android anteriores a Android 10 (nivel de API 29) permiten que las aplicaciones en segundo plano accedan a la información del portapapeles de la app en primer plano, lo que podría permitir el acceso directo a cualquier dato copiado por parte de personas malintencionadas.
- A partir de Android 12 (nivel de API 31), cada vez que una aplicación accede a los datos del portapapeles y los pega, se le muestra un mensaje de aviso al usuario, lo que dificulta que los ataques pasen desapercibidos. Además, para proteger la PII, Android admite la marca especial
ClipDescription.EXTRA_IS_SENSITIVE
o android.content.extra.IS_SENSITIVE
. Esto permite a los desarrolladores ofuscar visualmente la vista previa del contenido del portapapeles dentro de la GUI del teclado, lo que evita que los datos copiados se muestren visualmente en texto simple y que aplicaciones maliciosas los roben. No implementar una de las marcas mencionadas podría permitir que los atacantes extraigan datos sensibles copiados en el portapapeles a través de la técnica del hombro o de aplicaciones maliciosas que, mientras se ejecutan en segundo plano, toman capturas de pantalla o graban videos de las actividades de un usuario legítimo.
Impacto
El aprovechamiento de un manejo incorrecto del portapapeles podría provocar que actores maliciosos extraigan datos financieros o sensibles relacionados con el usuario. Esto puede ayudar a los atacantes a realizar más acciones, como campañas de phishing o robo de identidad.
Mitigaciones
Marcar datos sensibles
Esta solución se emplea para ofuscar visualmente la vista previa del contenido del portapapeles dentro de la GUI del teclado. Cualquier dato sensible que se pueda copiar, como contraseñas o datos de tarjetas de crédito, debe marcarse con ClipDescription.EXTRA_IS_SENSITIVE
o android.content.extra.IS_SENSITIVE
antes de llamar a 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);
Aplica la versión más reciente de Android
Si se aplica la aplicación para que se ejecute en versiones de Android posteriores o iguales a Android 10 (nivel de API 29), se evita que los procesos en segundo plano accedan a los datos del portapapeles en la aplicación en primer plano.
Para forzar que la app se ejecute solo en Android 10 (nivel de API 29) o versiones posteriores, establece los siguientes valores para la configuración de la versión en los archivos de compilación de Gradle dentro de tu proyecto en 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"
...
}
...
}
...
Borra el contenido del portapapeles después de un período definido
Si la aplicación está diseñada para ejecutarse en versiones de Android anteriores a Android 10 (nivel de API 29), cualquier aplicación en segundo plano puede acceder a los datos del portapapeles.
Para
reducir este riesgo, es útil implementar una función que borre los datos
copiados en el portapapeles después de un período específico. Esta función se realiza automáticamente a partir de Android 13 (nivel de API 33).
En versiones anteriores de Android, esta eliminación se puede realizar si se incluye el siguiente fragmento en el código de la aplicación.
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);
Recursos
El contenido y las muestras de código que aparecen en esta página están sujetas a las licencias que se describen en la Licencia de Contenido. Java y OpenJDK son marcas registradas de Oracle o sus afiliados.
Última actualización: 2025-07-26 (UTC)
[[["Fácil de comprender","easyToUnderstand","thumb-up"],["Resolvió mi problema","solvedMyProblem","thumb-up"],["Otro","otherUp","thumb-up"]],[["Falta la información que necesito","missingTheInformationINeed","thumb-down"],["Muy complicado o demasiados pasos","tooComplicatedTooManySteps","thumb-down"],["Desactualizado","outOfDate","thumb-down"],["Problema de traducción","translationIssue","thumb-down"],["Problema con las muestras o los códigos","samplesCodeIssue","thumb-down"],["Otro","otherDown","thumb-down"]],["Última actualización: 2025-07-26 (UTC)"],[],[],null,["# Secure Clipboard Handling\n\n\u003cbr /\u003e\n\n**OWASP category:** [MASVS-CODE: Code Quality](https://mas.owasp.org/MASVS/10-MASVS-CODE)\n\nOverview\n--------\n\nAndroid offers a powerful framework referred to as the [clipboard](/develop/ui/views/touch-and-input/copy-paste#Clipboard) for\ncopying and pasting data between applications. An improper implementation of\nthis feature could expose user-related data to unauthorized malicious actors or\napplications.\n\nThe specific risk associated with the exposure of clipboard data depends on the\nnature of the application and the Personal Identifiable Information (PII) it is\nhandling. The impact is especially high for financial applications, as they may\nexpose payment data, or apps that handle two-factor-authentication (2FA) codes.\n\nThe attack vectors that could be leveraged in order to exfiltrate clipboard data\nvary depending on Android version:\n\n- [Android versions older than Android 10 (API level 29)](/about/versions/10/privacy/changes#clipboard-data) allow background applications to access foreground app clipboard information, potentially allowing direct access to any copied data by malicious actors.\n- From Android 12 onwards (API level 31), every time an application accesses data within the clipboard and pastes it, a toast message is shown to the user, making it more difficult for attacks to go unnoticed. Additionally, in order to protect PII, Android supports the `ClipDescription.EXTRA_IS_SENSITIVE` or `android.content.extra.IS_SENSITIVE` special flag. This allows developers to visually obfuscate the clipboard content preview within the keyboard GUI, preventing copied data from being visually shown in clear-text and potentially stolen by malicious applications. Not implementing one of the aforementioned flags could in fact allow attackers to exfiltrate sensitive data copied to the clipboard by either shoulder surfing or through malicious applications that, while running in background, take screenshots or record videos of a legitimate user's activities.\n\nImpact\n------\n\nThe exploitation of improper clipboard handling could result in user-related\nsensitive or financial data being exfiltrated by malicious actors. This may aid\nattackers in conducting further actions such as phishing campaigns or identity\ntheft.\n\nMitigations\n-----------\n\n### Flag Sensitive Data\n\nThis solution is employed to visually obfuscate the clipboard content preview\nwithin the keyboard GUI. Any sensitive data that can be copied, such as\npasswords or credit card data, should be flagged with\n`ClipDescription.EXTRA_IS_SENSITIVE` or `android.content.extra.IS_SENSITIVE`\nbefore calling [`ClipboardManager.setPrimaryClip()`](/reference/android/content/ClipboardManager#setPrimaryClip(android.content.ClipData)). \n\n### Kotlin\n\n // If your app is compiled with the API level 33 SDK or higher.\n clipData.apply {\n description.extras = PersistableBundle().apply {\n putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true)\n }\n }\n\n // If your app is compiled with API level 32 SDK or lower.\n clipData.apply {\n description.extras = PersistableBundle().apply {\n putBoolean(\"android.content.extra.IS_SENSITIVE\", true)\n }\n }\n\n### Java\n\n // If your app is compiled with the API level 33 SDK or higher.\n PersistableBundle extras = new PersistableBundle();\n extras.putBoolean(ClipDescription.EXTRA_IS_SENSITIVE, true);\n clipData.getDescription().setExtras(extras);\n\n // If your app is compiled with API level 32 SDK or lower.\n PersistableBundle extras = new PersistableBundle();\n extras.putBoolean(\"android.content.extra.IS_SENSITIVE\", true);\n clipData.getDescription().setExtras(extras);\n\n### Enforce Latest Android Versions\n\nEnforcing the app to run on Android versions later or equal to Android 10 (API\n29) prevents background processes from accessing clipboard data in the\nforeground application.\n\nTo enforce the app to run only on Android 10 (API 29) or later, set the\nfollowing values for the version settings in the Gradle build files within your\nproject in Android Studio. \n\n### Groovy\n\n android {\n namespace 'com.example.testapp'\n compileSdk [SDK_LATEST_VERSION]\n\n defaultConfig {\n applicationId \"com.example.testapp\"\n minSdk 29\n targetSdk [SDK_LATEST_VERSION]\n versionCode 1\n versionName \"1.0\"\n ...\n }\n ...\n }\n ...\n\n### Kotlin\n\n android {\n namespace = \"com.example.testapp\"\n compileSdk = [SDK_LATEST_VERSION]\n\n defaultConfig {\n applicationId = \"com.example.testapp\"\n minSdk = 29\n targetSdk = [SDK_LATEST_VERSION]\n versionCode = 1\n versionName = \"1.0\"\n ...\n }\n ...\n }\n ...\n\n### Delete Clipboard content after a defined period of time\n\nIf the application is meant to run on Android versions lower than Android 10\n(API level 29), any background application can access clipboard data.\nIn order\nto reduce this risk, it's useful to implement a function that clears any data\ncopied to the clipboard after a specific period of time. This function is\n[automatically performed starting with Android 13 (API level 33)](https://blog.google/products/android/android-13/).\nFor older\nAndroid versions, this deletion can be performed by including the following\nsnippet within the application's code. \n\n### Kotlin\n\n //The Executor makes this task Asynchronous so that the UI continues being responsive\n backgroundExecutor.schedule({\n //Creates a clip object with the content of the Clipboard\n val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager\n val clip = clipboard.primaryClip\n //If SDK version is higher or equal to 28, it deletes Clipboard data with clearPrimaryClip()\n if (Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.P) {\n clipboard.clearPrimaryClip()\n } else if (Build.VERSION.SDK_INT \u003c Build.VERSION_CODES.P) {\n //If SDK version is lower than 28, it will replace Clipboard content with an empty value\n val newEmptyClip = ClipData.newPlainText(\"EmptyClipContent\", \"\")\n clipboard.setPrimaryClip(newEmptyClip)\n }\n //The delay after which the Clipboard is cleared, measured in seconds\n }, 5, TimeUnit.SECONDS)\n\n### Java\n\n //The Executor makes this task Asynchronous so that the UI continues being responsive\n\n ScheduledExecutorService backgroundExecutor = Executors.newSingleThreadScheduledExecutor();\n\n backgroundExecutor.schedule(new Runnable() {\n @Override\n public void run() {\n //Creates a clip object with the content of the Clipboard\n ClipboardManager clipboard = (ClipboardManager)getSystemService(Context.CLIPBOARD_SERVICE);\n ClipData clip = clipboard.getPrimaryClip();\n //If SDK version is higher or equal to 28, it deletes Clipboard data with clearPrimaryClip()\n if (Build.VERSION.SDK_INT \u003e= Build.VERSION_CODES.P) {\n clipboard.clearPrimaryClip();\n //If SDK version is lower than 28, it will replace Clipboard content with an empty value\n } else if (Build.VERSION.SDK_INT \u003c Build.VERSION_CODES.P) {\n ClipData newEmptyClip = ClipData.newPlainText(\"EmptyClipContent\", \"\");\n clipboard.setPrimaryClip(newEmptyClip);\n }\n //The delay after which the Clipboard is cleared, measured in seconds\n }, 5, TimeUnit.SECONDS);\n\nResources\n---------\n\n- [The clipboard framework](/develop/ui/views/touch-and-input/copy-paste#Clipboard)\n- [System notification shown when your app accesses clipboard data](/develop/ui/views/touch-and-input/copy-paste#PastingSystemNotifications)\n- [Add sensitive content to the clipboard](/develop/ui/views/touch-and-input/copy-paste#SensitiveContent)\n- [Privacy changes in Android 10](/about/versions/10/privacy/changes)\n- [Set app version information](/studio/publish/versioning#appversioning)"]]