A área de tecnologia se expande cada vez mais, tornando-se um mercado bastante aquecido para…
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).
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:
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.
Comments (0)