Skip to content

MASTG-TEST-0024: Teste de Permissões de Aplicativo

Deprecated Test

This test is deprecated and should not be used anymore. Reason: Nova versão disponível no MASTG V2

Please check the following MASTG v2 tests that cover this v1 test:

Visão Geral

Ao testar permissões de aplicativo))), o objetivo é tentar reduzir a quantidade de permissões utilizadas pelo seu aplicativo ao mínimo absoluto. Ao revisar cada permissão, lembre-se de que é uma prática recomendada primeiro tentar avaliar se o aplicativo precisa usar essa permissão, pois muitas funcionalidades, como tirar uma foto, podem ser realizadas sem ela, limitando o acesso a dados sensíveis. Se as permissões forem necessárias, certifique-se de que a solicitação/resposta para acessar a permissão seja tratada corretamente.

Análise Estática

Permissões Android

Verifique as permissões para garantir que o aplicativo realmente precise delas e remova as permissões desnecessárias. Por exemplo, a permissão INTERNET no arquivo AndroidManifest.xml é necessária para que uma Activity carregue uma página web em um WebView. Como um usuário pode revogar o direito de um aplicativo de usar uma permissão perigosa, o desenvolvedor deve verificar se o aplicativo possui a permissão apropriada sempre que uma ação que exija essa permissão for executada.

<uses-permission android:name="android.permission.INTERNET" />

Percorra as permissões com o desenvolvedor para identificar a finalidade de cada permissão definida e remova as permissões desnecessárias.

Além de revisar manualmente o arquivo AndroidManifest.xml, você também pode usar a ferramenta Android Asset Packaging (aapt) para examinar as permissões de um arquivo APK.

O aapt vem com o Android SDK na pasta build-tools. Ele requer um arquivo APK como entrada. Você pode listar os APKs no dispositivo executando adb shell pm list packages -f | grep -i <palavra-chave> como visto em Listando Aplicativos Instalados.

$ aapt d permissions app-x86-debug.apk
package: sg.vp.owasp_mobile.omtg_android
uses-permission: name='android.permission.WRITE_EXTERNAL_STORAGE'
uses-permission: name='android.permission.INTERNET'

Alternativamente, você pode obter uma lista mais detalhada de permissões via adb e a ferramenta dumpsys:

$ adb shell dumpsys package sg.vp.owasp_mobile.omtg_android | grep permission
    requested permissions:
      android.permission.WRITE_EXTERNAL_STORAGE
      android.permission.INTERNET
      android.permission.READ_EXTERNAL_STORAGE
    install permissions:
      android.permission.INTERNET: granted=true
      runtime permissions:

Consulte esta visão geral de permissões para descrições das permissões listadas que são consideradas perigosas.

READ_CALENDAR
WRITE_CALENDAR
READ_CALL_LOG
WRITE_CALL_LOG
PROCESS_OUTGOING_CALLS
CAMERA
READ_CONTACTS
WRITE_CONTACTS
GET_ACCOUNTS
ACCESS_FINE_LOCATION
ACCESS_COARSE_LOCATION
RECORD_AUDIO
READ_PHONE_STATE
READ_PHONE_NUMBERS
CALL_PHONE
ANSWER_PHONE_CALLS
ADD_VOICEMAIL
USE_SIP
BODY_SENSORS
SEND_SMS
RECEIVE_SMS
READ_SMS
RECEIVE_WAP_PUSH
RECEIVE_MMS
READ_EXTERNAL_STORAGE
WRITE_EXTERNAL_STORAGE

Permissões Personalizadas

Além de impor permissões personalizadas por meio do arquivo de manifesto do aplicativo, você também pode verificar as permissões programaticamente. No entanto, isso não é recomendado, pois é mais propenso a erros e pode ser contornado mais facilmente com, por exemplo, instrumentação em tempo de execução. É recomendado chamar o método ContextCompat.checkSelfPermission para verificar se uma activity tem uma permissão específica. Sempre que você vir um código como o trecho a seguir, certifique-se de que as mesmas permissões sejam impostas no arquivo de manifesto.

private static final String TAG = "LOG";
int canProcess = checkCallingOrSelfPermission("com.example.perm.READ_INCOMING_MSG");
if (canProcess != PERMISSION_GRANTED)
throw new SecurityException();

Ou com ContextCompat.checkSelfPermission, que compara com o arquivo de manifesto.

if (ContextCompat.checkSelfPermission(secureActivity.this, Manifest.READ_INCOMING_MSG)
        != PackageManager.PERMISSION_GRANTED) {
            //!= significa não igual a PERMISSION_GRANTED
            Log.v(TAG, "Permission denied");
        }

Solicitando Permissões

Se o seu aplicativo tiver permissões que precisam ser solicitadas em tempo de execução, o aplicativo deve chamar o método requestPermissions para obtê-las. O aplicativo passa as permissões necessárias e um código de solicitação inteiro que você especificou para o usuário de forma assíncrona, retornando assim que o usuário opta por aceitar ou negar a solicitação na mesma thread. Após a resposta ser retornada, o mesmo código de solicitação é passado para o método de callback do aplicativo.

private static final String TAG = "LOG";
// Começamos verificando a permissão da Activity atual
if (ContextCompat.checkSelfPermission(secureActivity.this,
        Manifest.permission.WRITE_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {

    // Permissão não concedida
    // Devemos mostrar uma explicação?
    if (ActivityCompat.shouldShowRequestPermissionRationale(secureActivity.this,
        // Obtém se você deve mostrar a UI com a razão para solicitar a permissão.
        // Você deve fazer isso apenas se não tiver a permissão e a razão da permissão solicitada não for comunicada claramente ao usuário.
            Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
        // A thread assíncrona aguarda a resposta do usuário.
        // Após o usuário ver a explicação, tente solicitar a permissão novamente.
    } else {
        // Solicita uma permissão que não precisa ser explicada.
        ActivityCompat.requestPermissions(secureActivity.this,
                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
        // MY_PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE será a constante inteira definida pelo aplicativo.
        // O método de callback obtém o resultado da solicitação.
    }
} else {
    // Permissão já concedida, mensagem de debug impressa no terminal.
    Log.v(TAG, "Permission already granted.");
}

Observe que, se você precisar fornecer qualquer informação ou explicação ao usuário, isso deve ser feito antes da chamada para requestPermissions, pois a caixa de diálogo do sistema não pode ser alterada após ser chamada.

Tratando Respostas às Solicitações de Permissão

Agora seu aplicativo deve substituir o método do sistema onRequestPermissionsResult para verificar se a permissão foi concedida. Este método recebe o inteiro requestCode como parâmetro de entrada (que é o mesmo código de solicitação criado em requestPermissions).

O seguinte método de callback pode ser usado para WRITE_EXTERNAL_STORAGE.

@Override // Necessário para substituir o método do sistema onRequestPermissionsResult()
public void onRequestPermissionsResult(int requestCode, // requestCode é o que você especificou em requestPermissions()
        String permissions[], int[] grantResults) {
    switch (requestCode) {
        case MY_PERMISSIONS_WRITE_EXTERNAL_STORAGE: {
            if (grantResults.length > 0
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 0 é uma solicitação cancelada, se o array de inteiros for igual ao requestCode, a permissão é concedida.
            } else {
                // o código de permissão negada vai aqui.
                Log.v(TAG, "Permission denied");
            }
            return;
        }
        // Outros casos de switch podem ser adicionados aqui para verificações de múltiplas permissões.
    }
}

As permissões devem ser explicitamente solicitadas para cada permissão necessária, mesmo que uma permissão similar do mesmo grupo já tenha sido solicitada. Para aplicativos direcionados ao Android 7.1 (nível de API 25) e mais antigos, o Android concederá automaticamente a um aplicativo todas as permissões de um grupo de permissões, se o usuário conceder uma das permissões solicitadas desse grupo. A partir do Android 8.0 (nível de API 26), as permissões ainda serão concedidas automaticamente se o usuário já tiver concedido uma permissão do mesmo grupo de permissões, mas o aplicativo ainda precisa solicitar explicitamente a permissão. Nesse caso, o manipulador onRequestPermissionsResult será acionado automaticamente sem qualquer interação do usuário.

Por exemplo, se tanto READ_EXTERNAL_STORAGE quanto WRITE_EXTERNAL_STORAGE estiverem listados no Android Manifest, mas apenas as permissões para READ_EXTERNAL_STORAGE forem concedidas, então solicitar WRITE_EXTERNAL_STORAGE terá permissões automaticamente sem interação do usuário, pois estão no mesmo grupo e não foram explicitamente solicitadas.

Análise de Permissões

Sempre verifique se o aplicativo está solicitando permissões que realmente necessita. Certifique-se de que nenhuma permissão não relacionada ao objetivo do aplicativo seja solicitada, especialmente permissões DANGEROUS e SIGNATURE, pois elas podem afetar tanto o usuário quanto o aplicativo se forem mal manipuladas. Por exemplo, deve ser suspeito se um aplicativo de jogo para um jogador exigir acesso a android.permission.WRITE_SMS.

Ao analisar permissões, você deve investigar os cenários de casos de uso concretos do aplicativo e sempre verificar se existem APIs substitutas para qualquer permissão DANGEROUS em uso. Um bom exemplo é a SMS Retriever API, que simplifica o uso de permissões de SMS ao realizar verificação de usuário baseada em SMS. Ao usar essa API, um aplicativo não precisa declarar permissões DANGEROUS, o que é um benefício tanto para o usuário quanto para os desenvolvedores do aplicativo, que não precisam enviar o Formulário de Declaração de Permissões.

Análise Dinâmica

As permissões para aplicativos instalados podem ser recuperadas com adb. O seguinte extrato demonstra como examinar as permissões usadas por um aplicativo.

$ adb shell dumpsys package com.google.android.youtube
...
declared permissions:
  com.google.android.youtube.permission.C2D_MESSAGE: prot=signature, INSTALLED
requested permissions:
  android.permission.INTERNET
  android.permission.ACCESS_NETWORK_STATE
install permissions:
  com.google.android.c2dm.permission.RECEIVE: granted=true
  android.permission.USE_CREDENTIALS: granted=true
  com.google.android.providers.gsf.permission.READ_GSERVICES: granted=true
...

A saída mostra todas as permissões usando as seguintes categorias:

  • declared permissions: lista de todas as permissões personalizadas.
  • requested and install permissions: lista de todas as permissões de instalação, incluindo permissões normais e de assinatura.
  • runtime permissions: lista de todas as permissões perigosas.

Ao fazer a análise dinâmica:

  • Avalie se o aplicativo realmente precisa das permissões solicitadas. Por exemplo: um jogo para um jogador que exige acesso a android.permission.WRITE_SMS pode não ser uma boa ideia.
  • Em muitos casos, o aplicativo poderia optar por alternativas à declaração de permissões, como:
    • solicitar a permissão ACCESS_COARSE_LOCATION em vez de ACCESS_FINE_LOCATION. Ou melhor ainda, não solicitar a permissão e, em vez disso, pedir ao usuário para inserir um código postal.
    • invocar a ação de intent ACTION_IMAGE_CAPTURE ou ACTION_VIDEO_CAPTURE em vez de solicitar a permissão CAMERA.
    • usar Companion Device Pairing (Android 8.0 (nível de API 26) e superior) ao emparelhar com um dispositivo Bluetooth em vez de declarar as permissões ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATIION ou BLUETOOTH_ADMIN.
  • Use o Painel de Privacidade (Android 12 (nível de API 31) e superior) para verificar como o aplicativo explica o acesso a informações sensíveis.

Para obter detalhes sobre uma permissão específica, você pode consultar a Documentação do Android.