Categoría de OWASP: MASVS-CODE: Calidad de código
Descripción general
Usar PendingIntent.getCreator*()
o PendingIntent.getTarget*()
para determinar si se debe confiar en el remitente de un PendingIntent crea un riesgo de explotación.
PendingIntent.getCreator*()
o PendingIntent.getTarget*()
devuelven el creador del PendingIntent, que no siempre coincide con su remitente. Se puede confiar en el creador, pero nunca se debe confiar en el remitente, ya que podría ser una app maliciosa que adquirió el PendingIntent de otra app con diversos mecanismos, por ejemplo:
- desde
NotificationListenerService
- casos de uso legítimos que forman parte de la app vulnerable
Un ejemplo de uso legítimo de PendingIntent.getCreator*()
o PendingIntent.getTarget*()
sería mostrar el ícono de la app que iniciará el PendingIntent.
Impacto
Confiar en el remitente de un PendingIntent porque consultaste (y confías en) el creador puede generar vulnerabilidades. Si una app confía en el remitente del PendingIntent según su creador y, luego, comparte su lógica de autenticación o autorización, cada vez que el remitente del PendingIntent sea una app maliciosa, esto generará una omisión de la autenticación o, incluso, una posible ejecución de código remoto basada en una entrada no válida y no confiable, según la implementación del código de la aplicación vulnerable.
Mitigaciones
Distingue entre el remitente y el creador
Cualquier tipo de lógica de autenticación o autorización que se realice cuando se recibe un PendingIntent no debe basarse en suposiciones sobre el creador del PendingIntent identificado con PendingIntent.getCreator*()
o PendingIntent.getTarget*()
.
Usa formas alternativas de validar a los llamadores
Si necesitas autenticar al llamador, en lugar de usar PendingIntent, debes usar un Service o ContentProvider. Ambos permiten recuperar el UID del llamador con Binder.getCallingUid() cuando estás en el contexto de enviar un IPC entrante. El UID se puede consultar más adelante con PackageManager.getPackagesForUid().
Otro enfoque, disponible desde el nivel de API 34, sería usar BroadcastReceiver.getSentFromUid() o BroadcastReceiver.getSentFromPackage() si el emisor habilitó el uso compartido de la identidad durante la transmisión con BroadcastOptions.isShareIdentityEnabled().
Siempre debes verificar si el paquete de llamada tiene la firma esperada, ya que los paquetes transferidos de forma local pueden tener nombres de paquete que se superponen con los de Play Store.