Skip to content
Simulação de configuração de acesso via SMS

Validação de Código de Acesso via SMS com Android

O uso de códigos de acesso de uso único é um mecanismo bastante comum de validação de identidade de usuários em aplicações. Com aplicações móveis sendo cada vez mais o principal ponto de acesso de usuários a aplicações, torna-se mais comum o recebimento deste tipo de código de validação por meio de SMS. Do ponto de vista de segurança, esta prática ajuda a garantir a identidade do usuário e a oferecer mais uma camada de proteção contra acessos indevidos à aplicação.

Contudo, do ponto de vista de experiência do usuário, a operação de sair da aplicação para acessar o SMS recebido, copiar ou memorizar o código e digitá-lo novamente na aplicação é uma ação tediosa e bastante suscetível a erros.

Assim, uma prática também comum que passou a ser adotada em aplicações móveis é a detecção do recebimento do SMS de validação e o preenchimento automático do código de validação na interface da aplicação, poupando o usuário de realizar manualmente esta ação.

O framework Android possui permissões específicas para realização de leitura de mensagens de texto do aparelho do usuário. A aplicação pode, por meio de um objeto BroadcastReceiver e de um IntentFilter para a ação de recebimento de SMS, interceptar as mensagens recebidas pelo usuário e tratar o conteúdo para realizar a leitura do código recebido.

Esta abordagem requer que o usuário conceda explicitamente a permissão de acesso ao SMS por parte do aplicativo, que, com esta permissão passa a ter acesso não só a ler o SMS recebido com o código de acesso, como todo e qualquer outro SMS que o usuário venha a receber (o que justifica a necessidade de o usuário explicitamente autorizar este acesso).

Porém, em outubro de 2018, por meio de um post em seu blog oficial, o Google anunciou que passaria a limitar o acesso de aplicações a funcionalidades de telefonia (SMS e ligações):

“As part of today’s Google Play Developer Policy update, we’re announcing changes related to SMS and Call Log permissions. Some Android apps ask for permission to access a user’s phone (including call logs) and SMS data. Going forward, Google Play will limit which apps are allowed to ask for these permissions. Only an app that has been selected as a user’s default app for making calls or text messages will be able to access call logs and SMS, respectively.”

Basicamente, passa a ser permitido o acesso às funcionalidades de telefonia em aplicações Android apenas para aplicações cuja funcionalidade principal esteja diretamente ligada a envio/recebimento de SMS e ligações e que tenham sido definidas pelo usuário como aplicações padrão para este tipo de funcionalidade.

A SMS Retriever API

Como alternativa para a abordagem anterior, o Google passou a oferecer, por meio do Google Play Services (versão 10.2+), a SMS Retreiver API, como forma de permitir que aplicações continuem tendo acesso a funcionalidade de obtenção automática de conteúdo de SMS (como código de validação, por exemplo), sem infringir as novas determinações de segurança.
Com esta API, a aplicação terá acesso apenas aos SMS que sejam destinados a ela, com a vantagem de não precisar de permissões explícitas do usuário. Isto é possível porque o app somente poderá interceptar e manipular os SMS que possuam em seu conteúdo uma hash específica, gerada a partir do certificado digital que assina o APK do aplicativo (figura 1).

Exemplo da estrutura do SMS
Figura 1: Exemplo da estrutura do SMS

A seguir, veremos como implementar e testar a captura de código de validação utilizando a SMS Retriever API. Neste artigo, a implementação se dará por meio da linguagem Kotlin. Os passos descritos a seguir envolvem apenas as operações referentes ao SMS Retriever. O código completo do projeto de exemplo pode ser baixado aqui.

1º Passo – Dependências

O primeiro passo para implementação é adicionar ao arquivo build.gradle a um projeto de aplicação Android as dependências do Google Play Services referentes à SMS Retriever API:

implementation ‘com.google.android.gms:play-services-auth:18.0.0’

implementation ‘com.google.android.gms:play-services-auth-api-phone:17.4.0’

2º Passo – Inicialização do SMS Retriever Client

Para receber os SMS destinados ao aplicativo, é preciso inicializar um objeto SMSRetrieverClient. Este objeto inicia um objeto AsyncTask que ouvirá, pelos próximos 5 minutos, o recebimento de SMS pelo aplicativo que contenha a hash compatível com o app. Este processo não irá solicitar a permissão do usuário.

val smsRetrieverClient = SmsRetriever.getClient(this)

val receiverTask = smsRetrieverClient.startSmsRetriever()

 

receiverTask.addOnSuccessListener {

Toast.makeText(this,

R.string.successMessageSmsRetrieverStartup,

Toast.LENGTH_LONG

).show()

}

 

receiverTask.addOnFailureListener {

Toast.makeText(this,

R.string.errorMessageSmsRetrieverStartup,

Toast.LENGTH_LONG

).show()

}


 

Por meio dos listeners (linhas 4 e 11), é possível identificar se foi possível iniciar o SMSRetrieverClient, e prover um tratamento adequado em caso de erro.

3º Passo – Definição do Objeto BroadcastReceiver

Uma vez que o app intercepte o SMS com a hash desejada, o mesmo recebe do Play Services um Intent com a action “com.google.android.gms.auth.api.phone.SMS_RETRIEVED”, contendo também o status da operação e o conteúdo do SMS recebido.

private val smsBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == SmsRetriever.SMS_RETRIEVED_ACTION) {
val status = intent.
getParcelableExtra(SmsRetriever.EXTRA_STATUS) as Status

when (status.statusCode) {
CommonStatusCodes.SUCCESS -> {
val message = intent
.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
message?.let { getValidationCodeFromSms(it) }
}
CommonStatusCodes.TIMEOUT ->
Toast.makeText(this@MainActivity,
R.string.errorMessageSmsTimeout,
Toast.LENGTH_LONG
).show()
}
}
}
}

Esta action ocorre tanto ao receber o SMS quanto ao finalizar o prazo de 5 minutos do SMSRetrieverClient. Portanto, é importante tratar o código de retorno recebido pelo Intent no extra SmsRetriever.EXTRA_STATUS, conforme as linhas 8 e 13 no código acima.

Caso o SMS tenha sido recebido com sucesso, basta obter a mensagem recebida pelo Intent no extra SmsRetriever.EXTRA_SMS_MESSAGE (linha 9) e extrair o código de validação.

4º Passo – Extração do Código de Validação do SMS Recebido

Uma vez que o SMS tenha sido recebido, basta tratar seu conteúdo e extrair o código de validação recebido.

private fun getValidationCodeFromSms(message: String) {
val colonPosition = message.indexOf(CHAR_COLON) + CODE_OFFSET

val token = message.substring(colonPosition, colonPosition + CODE_SIZE)

editTextCode.setText(token)
validateCode()
}

Neste exemplo, espera-se receber a mensagem no formato: “Seu código de acesso é: XXXX”. Assim, no código acima (linha 2), é obtida, a partir do texto do SMS, uma substring a partir da posição do primeiro caractere após o caractere de dois-pontos até o tamanho do código de validação (no exemplo, 4 caracteres).

Na sequência o campo de entrada da tela é preenchido e o processo de validação é disparado, sem a necessidade de mais uma ação explícita do usuário.

5º Passo – Início/Pausa do BroadcastReceiver

Por fim, nos métodos onResume e onPause da Activity, deve-se, respectivamente, registrar e remover o objeto do BroadcastReceiver. Assim, o mesmo estará ativo apenas enquanto a Activity estiver em execução.

override fun onResume() {
super.onResume()
registerReceiver(smsBroadcastReceiver,
IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION))
}

override fun onPause() {
super.onPause()
unregisterReceiver(smsBroadcastReceiver)
}

Gerando a Hash do SMS e Testando o Exemplo

O processo de geração da hash que identificará os SMS destinados ao app deverá ser realizada a partir do keystore utilizado na geração do APK. Para o APK de release, já publicado na Play Store, deve-se baixar o certificado de assinatura do app a partir do Google Play Console e importar o certificado em uma chave:

keytool -importcert -file deployment_cert.der -keystore temporary.keystore -alias PlayDeploymentCert

Em debug, o Android Studio utiliza um certificado padrão para assinatura do APK, localizado na pasta c:Usersusername.androiddebug.keystore. O cálculo da hash pode ser realizado utilizando este keystore, ou, preferencialmente, pode-se gerar um novo keystore específico para esta finalidade, utilizando o keytool:

keytool -genkey -alias SMSRetrieverAlias -keyalg RSA -keypass 123456 -storepass 123456 -keystore SMSRetrieverKey.jks

Para configurar o novo keystore em debug, basta incluir a configuração no arquivo build.gradle:

android {
signingConfigs {
debug {
storeFile file('\caminho\para\SMSRetrieverKey.jks')
storePassword '123456'
keyAlias = 'SMSRetrieverAlias'
keyPassword '123456'
}
}

// continuação das configurações
}

Para obtenção da hash, o Google disponibiliza neste link um script que realiza a operação de maneira facilitada, a partir do keystore e do pacote do app (no Windows, este script deverá ser executado a partir do PowerShell), conforme o comando abaixo:

./sms_retriever_hash.sh --package package_name --keystore keystore_file

Para simular o envio do SMS para testes via emulador é possível utilizar as funcionalidades de telefonia na opção “Extended Controls” do emulador:

Simulação de envio do SMS
Figura 2 – Simulação de envio do SMS

O app receberá o SMS enviado pelo simulador e realizará o tratamento do código de acesso, conforme o exemplo de teste abaixo:

Conclusão

A utilização da SMS Retriever API permite uma abordagem mais segura e direcionada no processo de recebimento e tratamento de SMS por parte do aplicativo. Para o desenvolvedor, há a certeza de que o BroadcastReceiver somente será acionado para mensagens destinadas ao aplicativo, facilitando o tratamento e processamento destas mensagens.

Além disso, não há a necessidade de código para tratamento de solicitação de permissões do usuário para a leitura do SMS. Do ponto de vista do usuário final há a segurança de que mensagens privadas não serão acessadas de forma indevida sem o seu conhecimento.

Compartilhe:

Comments (0)

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Back To Top
Code Journey
Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.