MASTG-KNOW-0024: Intenções Pendentes

Frequentemente, ao lidar com fluxos complexos durante o desenvolvimento de aplicativos, surgem situações em que um app A deseja que outro app B execute uma determinada ação no futuro, em nome do app A. Tentar implementar isso usando apenas Intents leva a diversos problemas de segurança, como ter múltiplos componentes exportados. Para lidar com esse caso de uso de forma segura, o Android fornece a API PendingIntent.

Os PendingIntents são mais comumente usados para notificações, widgets de aplicativo, serviços de navegação de mídia, etc. Quando usados para notificações, o PendingIntent é utilizado para declarar uma intenção a ser executada quando o usuário realiza uma ação com a notificação de um aplicativo. A notificação requer um callback para o aplicativo para acionar uma ação quando o usuário clica nela.

Internamente, um objeto PendingIntent encapsula um objeto Intent normal (referido como intent base) que será eventualmente usado para invocar uma ação. Por exemplo, o intent base especifica que uma atividade A deve ser iniciada em um aplicativo. O aplicativo receptor do PendingIntent desencapsulará e recuperará esse intent base e invocará a atividade A chamando a função PendingIntent.send.

Uma implementação típica para usar PendingIntent é mostrada abaixo:

Intent intent = new Intent(applicationContext, SomeActivity.class);     // intent base

// cria um pending intent
PendingIntent pendingIntent = PendingIntent.getActivity(applicationContext, 0, intent, PendingIntent.FLAG_IMMUTABLE);

// envia o pending intent para outro app
Intent anotherIntent = new Intent();
anotherIntent.setClassName("other.app", "other.app.MainActivity");
anotherIntent.putExtra("pendingIntent", pendingIntent);
startActivity(anotherIntent);

O que torna um PendingIntent seguro é que, diferentemente de um Intent normal, ele concede permissão a um aplicativo externo para usar o Intent (o intent base) que contém, como se estivesse sendo executado pelo próprio processo do seu aplicativo. Isso permite que um aplicativo os use livremente para criar callbacks sem a necessidade de criar atividades exportadas.

Se não for implementado corretamente, um aplicativo malicioso pode sequestrar um PendingIntent. Por exemplo, no caso de notificação mencionado acima, um aplicativo malicioso com android.permission.BIND_NOTIFICATION_LISTENER_SERVICE pode vincular-se ao serviço de listener de notificação e recuperar o pending intent.

Existem certas armadilhas de segurança ao implementar PendingIntents, listadas abaixo:

  • Campos mutáveis: Um PendingIntent pode ter campos mutáveis e vazios que podem ser preenchidos por um aplicativo malicioso. Isso pode levar a que um aplicativo malicioso obtenha acesso a componentes não exportados do aplicativo. Usar a flag PendingIntent.FLAG_IMMUTABLE torna o PendingIntent imutável e impede qualquer alteração nos campos. Antes do Android 12 (nível de API 31), o PendingIntent era mutável por padrão, enquanto a partir do Android 12 (nível de API 31) ele foi alterado para imutável por padrão para evitar vulnerabilidades acidentais.

  • Uso de intent implícito: Um aplicativo malicioso pode receber um PendingIntent e atualizar o intent base para direcionar o componente e o pacote dentro do aplicativo malicioso. Como mitigação, certifique-se de especificar explicitamente o pacote exato, a ação e o componente que receberão o intent base.

O caso mais comum de ataque com PendingIntent ocorre quando um aplicativo malicioso é capaz de interceptá-lo.

Para mais detalhes, consulte a documentação do Android sobre usando um pending intent.