Skip to content

MASTG-KNOW-0001: Autenticação Biométrica

A autenticação biométrica é um mecanismo conveniente para autenticação, mas também introduz uma superfície de ataque adicional ao ser utilizada. A documentação para desenvolvedores Android oferece uma visão geral interessante e indicadores para medir a segurança do desbloqueio biométrico.

A plataforma Android oferece três classes diferentes para autenticação biométrica:

  • Android 10 (API level 29) e superior: BiometricManager
  • Android 9 (API level 28) e superior: BiometricPrompt
  • Android 6.0 (API level 23) e superior: FingerprintManager (obsoleto no Android 9 (API level 28))

A classe BiometricManager pode ser usada para verificar se o hardware biométrico está disponível no dispositivo e se foi configurado pelo usuário. Se for esse o caso, a classe BiometricPrompt pode ser usada para exibir um diálogo biométrico fornecido pelo sistema.

A classe BiometricPrompt é uma melhoria significativa, pois permite ter uma interface de usuário consistente para autenticação biométrica no Android e também suporta mais sensores além da impressão digital.

Uma visão geral e explicação muito detalhada da API Biométrica no Android foi publicada no Android Developer Blog.

Exibir um diálogo de autenticação biométrica

Biblioteca Biométrica

O Android fornece uma biblioteca chamada Biometric (consulte também a Referência da API androidx.biometric) que oferece uma versão de compatibilidade das APIs BiometricPrompt e BiometricManager, conforme implementado no Android 10, com suporte completo de recursos até o Android 6.0 (API 23).

Você pode encontrar uma implementação de referência e instruções sobre como exibir um diálogo de autenticação biométrica na documentação para desenvolvedores Android.

Existem dois métodos authenticate disponíveis na classe BiometricPrompt. Um deles espera um CryptoObject, que adiciona uma camada adicional de segurança para a autenticação biométrica.

O fluxo de autenticação seria o seguinte ao usar CryptoObject:

  • O aplicativo cria uma chave no KeyStore com setUserAuthenticationRequired e setInvalidatedByBiometricEnrollment definidos como true. Além disso, setUserAuthenticationValidityDurationSeconds deve ser definido como -1.
  • Essa chave é usada para criptografar informações que autenticam o usuário (por exemplo, informações de sessão ou token de autenticação).
  • Um conjunto válido de biometria deve ser apresentado antes que a chave seja liberada do KeyStore para descriptografar os dados, o que é validado através do método authenticate e do CryptoObject.
  • Esta solução não pode ser contornada, mesmo em dispositivos com root, pois a chave do KeyStore só pode ser usada após autenticação biométrica bem-sucedida.

Se CryptoObject não for usado como parte do método de autenticação, ele pode ser contornado usando Frida. Consulte a seção "Instrumentação Dinâmica" para mais detalhes.

Os desenvolvedores podem usar várias classes de validação oferecidas pelo Android para testar a implementação da autenticação biométrica em seu aplicativo.

Autenticação Biométrica para Proteção de Dados ou Operações Sensíveis

O fluxo de confirmação de credenciais está disponível desde o Android 6.0 e é usado para garantir que os usuários não precisem inserir senhas específicas do aplicativo junto com a proteção da tela de bloqueio. Em vez disso: se um usuário fez login no dispositivo recentemente, a confirmação de credenciais pode ser usada para desbloquear materiais criptográficos do AndroidKeystore. Ou seja, se o usuário desbloqueou o dispositivo dentro dos limites de tempo definidos (setUserAuthenticationValidityDurationSeconds), caso contrário, o dispositivo precisa ser desbloqueado novamente.

Observe que a segurança da Confirmação de Credenciais é tão forte quanto a proteção definida na tela de bloqueio. Isso geralmente significa que padrões de tela de bloqueio previsíveis são usados e, portanto, não recomendamos que nenhum aplicativo que exija controles de segurança de nível L2 use Confirmação de Credenciais.

Verifique se a tela de bloqueio está definida:

KeyguardManager mKeyguardManager = (KeyguardManager) getSystemService(Context.KEYGUARD_SERVICE);
if (!mKeyguardManager.isKeyguardSecure()) {
    // Exibir uma mensagem de que o usuário não configurou uma tela de bloqueio.
}
  • Crie a chave protegida pela tela de bloqueio. Para usar essa chave, o usuário precisa ter desbloqueado o dispositivo nos últimos X segundos, ou o dispositivo precisa ser desbloqueado novamente. Certifique-se de que esse tempo limite não seja muito longo, pois fica mais difícil garantir que foi o mesmo usuário usando o aplicativo que desbloqueou o dispositivo:

    try {
        KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
        keyStore.load(null);
        KeyGenerator keyGenerator = KeyGenerator.getInstance(
                KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
    
        // Define o alias da entrada no Android KeyStore onde a chave aparecerá
        // e as restrições (propósitos) no construtor do Builder
        keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_NAME,
                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
                .setUserAuthenticationRequired(true)
                        // Exige que o usuário tenha desbloqueado nos últimos 30 segundos
                .setUserAuthenticationValidityDurationSeconds(30)
                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
                .build());
        keyGenerator.generateKey();
    } catch (NoSuchAlgorithmException | NoSuchProviderException
            | InvalidAlgorithmParameterException | KeyStoreException
            | CertificateException | IOException e) {
        throw new RuntimeException("Failed to create a symmetric key", e);
    }
    
  • Configure a tela de bloqueio para confirmar:

    private static final int REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS = 1; //usado como número para verificar se é daqui que os resultados da atividade vêm
    Intent intent = mKeyguardManager.createConfirmDeviceCredentialIntent(null, null);
    if (intent != null) {
        startActivityForResult(intent, REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS);
    }
    
  • Use a chave após a tela de bloqueio:

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CODE_CONFIRM_DEVICE_CREDENTIALS) {
            // Desafio concluído, prossiga com o uso da cifra
            if (resultCode == RESULT_OK) {
                //use a chave para o fluxo de autenticação real
            } else {
                // O usuário cancelou ou não concluiu a operação
                // da tela de bloqueio. Siga para o fluxo de erro/cancelamento.
            }
        }
    }
    

Invalidando Chaves

O Android 7.0 (API level 24) adiciona o método setInvalidatedByBiometricEnrollment(boolean invalidateKey) ao KeyGenParameterSpec.Builder. Quando o valor invalidateKey é definido como true (o padrão), as chaves válidas para autenticação por impressão digital são invalidadas irreversivelmente quando uma nova impressão digital é cadastrada. Isso impede que um invasor recupere a chave, mesmo que consiga cadastrar uma impressão digital adicional.

SDKs de Terceiros para Biometria

Certifique-se de que a autenticação por impressão digital e/ou outros tipos de autenticação biométrica sejam baseados exclusivamente no SDK do Android e em suas APIs. Se não for o caso, garanta que o SDK alternativo tenha sido devidamente avaliado quanto a possíveis vulnerabilidades. Certifique-se de que o SDK seja respaldado pelo TEE/SE que libera um segredo (criptográfico) com base na autenticação biométrica. Esse segredo não deve ser desbloqueado por nada além de uma entrada biométrica válida. Dessa forma, nunca deve acontecer de a lógica de impressão digital ser contornada.