Segurança das aplicações Android
Ao contrário do que se possa pensar, a segurança é um mito. Não existem soluções totalmente seguras. Basta olhar para os últimos meses, com o ataque à infra-estrutura da Sony, o ataque às centrifugadoras de urânio iranianas ou ao mediático conflito entre a Apple e o FBI, que trouxe para a estrelato a empresa israelita Cellebrite no dito “impossível” desbloqueio do iPhone.
Tudo se resume a uma questão de empenho, tanto em recursos como em tempo, para conseguir quebrar e entrar em qualquer sistema. Exemplos disso, por excelência, são os cofres-fortes. Explicando de forma resumida, a avaliação destes é feita com base no tempo de que um “ladrão” profissional necessita para o arrombar usando processos mecânicos e elétricos, sem destruir o conteúdo. Um cofre da classe “TL-15” tem que, de acordo com a norma aguentar 15 minutos de tortura, com instrumentos mecânicos e/ou eléctricos, antes de ser aberto.
O segredo na escolha de um bom sistema de segurança, tanto no mundo real como no mundo digital, é encontrar uma solução que seja mais difícil de penetrar e eleve o risco de se ser apanhado, de forma a que o nosso ladrão não veja uma oportunidade, mas sim uma fonte de aborrecimentos. Precisamos de mostrar o seu atrevimento lhe vai sair caro.
Dito isto, falando agora no mundo digital e mais precisamente em aplicações Android, há medidas simples que podemos tomar para dificultar a vida aos piratas. E o que é mesmo uma aplicação Android? Não é mais do que um ficheiro comprimido, do formato ZIP, com a extensão apk (Android Package), que é lido pelo instalador do Android, responsável por definir espaço, criar pastas e copiar os ficheiros nos devidos destinos para depois ser executado pelo sistema operativo quando for solicitado pelo utilizador.
Dentro de um Android Package típico, temos a seguinte estrutura:
A pasta assets contem todos os ficheiros anexos, como imagens, icons ou ficheiros de ajuda. Na directoria META-INF, ficam as assinaturas dos ficheiros contidos no apk e, no directório res, temos os elementos gráficos, icons e splash, usados pelo Launcher do Android.
As definições e os privilégios necessários para correr a aplicação ficam gravados no AndroidManifest.xml, e o mapeamento dos elementos gráficos usado no código ficam no resources.arsc. Por fim, e não menos importante, temos o classes.dex (Dalvik Executable) com o código compilado.
É aqui que o assunto se torna interessante. Uma aplicação nativa Android terá todo o código compilado no classes.dex e as imagens, entre outros recursos utilizados, localizadas na pasta assets.
Uma das características do Android é não usar a clássica máquina virtual JAVA, mas sim uma nova máquina virtual intitulada Dalvik, criada de raiz, pensada e optimizada para aumentar a velocidade e reduzir tamanhos. Não é usado o bytecode Java. Em vez disso, foi introduzido o DEX. Por exemplo, numa aplicação JAVA, cada classe é compilada para um ficheiro class individual, enquanto que, no DEX, todas as classes ficam juntas num único ficheiro. Outra vantagem do DEX é o tamanho, geralmente mais de 50% inferior ao JAR descomprimido. Apesar destas diferenças, é possível converter o DEX em JAR. Temos várias aplicações freeware disponíveis para o efeito. A mais conhecida é o dex2jar, disponível no Sourceforge, entre outros repositórios. Fazemos o download, descomprimimos o pacote e executamos a aplicação, apontando para o nosso apk.
Descomprimimos o ficheiro JAR, também ele no formato ZIP, e usamos um de muitos descompiladores disponíveis para o JAVA para convertar o ficheiro class. Pessoalmente gosto de usar o JD-GUI Java Decompiler, de Emmanuel Dupuy .
Pegando numa aplicação híbrida, baseada no PhoneGap, o processo é muito mais simples. Esta terá todo o seu código, html e javascript, em texto livre, distribuído na pasta assets, deixando os plugins do PhoneGap no classes.dex.
Com isto tudo, podemos alterar o código e reverter o processo. Compilamos, criamos o JAR, convertemos para DEX, com ajuda do d2j-jar2dex. Precisamos de voltar a criar os hash de cada ficheiro, com base no jarsigner, e compactar tudo num novo ficheiro apk.
Como obter um apk? Para quem tiver o adb instalado, podemos ligar ao dispositivo e puxar diretamente o apk.
Para quem tiver pressa, podemos usar aplicações disponíveis do googlePlay, como o APKoptic ou o APK Extractor (Backup Apk) e gravar o ficheiro num cartão sd para depois transferir para o nosso pc.
Como saber o id do apk que pretendemos analisar? Ao navegar, no googlePlay, até à página de apresentação do produto, conseguimos ler, no url do browser, o seu id.
https://play.google.com/store/apps/details?id=exemplo.app.pt.apk
Nos outros ecossistemas, iOS, blackberry e Windows, o processo é semelhante, apesar do nível de dificuldade ser maior. De facto a Apple dedica muita energia à segurança. As aplicações são digitalmente assinadas na Apple Store para serem instaladas num dispositivo específico. O controlo é muito maior, e isso incluí os programadores. Mas, há sempre um mas, ao fazer jailbreak de um iPhone, deitamos por terra esta barreira.
Será possível proteger as nossas aplicações ou temos que aceitar impavidamente esta situação? A resposta é nim. Não é possível ter uma garantia total. No entanto, é possível prevenir. É possível tornar a tarefa de tal modo hercúlea que faça desistir qualquer aprendiz a hacker.
Primeiro, a segurança deve ser pensada desde o início. Devemos conhecer os mecanismos de defesa que a nossa plataforma de eleição oferece e desenhar a nossa aplicação nesse sentido. Não se pode começar a codificar e esperar que a ofuscação do código faça milagres. Devemos assumir que o nosso código vai ser visto. Logo, ofuscar o código é essencial. Não resolve o problema, mas faz parte da solução. Para aplicações nativas, no Android Studio, basta configurar e ativar o Proguard. Para aplicações hìbridas, podemos usar o Google Closure Compiler com o nível de compilação ADVANCED_OPTIMIZATIONS.
Mesmo ofuscado, temos outro problema. Strings e valores de configuração, como por exemplo endereços de acesso e chaves de encriptação, estão disponíveis aos olhares mais indiscretos. O seu lugar, definitivamente, não é no código. Devem estar fora, idealmente criados na instalação, no “Internal Storage”.
O “Internal Storage” é um espaço criado pelo Android, de acesso exclusivo da aplicação, não sendo acessível aos outros processos em actividade, nem mesmo ao utilizador. Mesmo assim, num dispositivo rooteado, tal não é garantia de segurança. É possível corromper o sistema operativo e aceder ao “Internal Storage” . Aí precisamos de encriptar os dados. Hoje em dia, os sistemas em chip (SoC) possuem um Trusted Execution Environment (TEE). Trata-se, com respetivas diferenças de cada implementação, de cada fabricante, de um processador virtual com capacidade para criar e gerir chaves privadas, totalmente inacessível o resto dos processos em curso. Aliás é geralmente esse módulo que gere as chaves usadas pelas placas wifi desses mesmos SoC.
O uso não podia ser mais simples. Criamos uma chave, que fica armazenada internamente e segura dentro do chip.
Podemos criar as chaves de que precisamos como, por exemplo, uma para o Internal Storage, onde codificamos tudo e garantimos que os dados só serão usados nesse equipamento. Podemos ter outro certificado para as comunicações com o servidor XPTO, em que fornecemos a chave pública ao servidor.
Comprometer uma KeyStore é possível, mas entramos numa outra dimensão no mundo da segurança. Cada fabricante terá a sua implementação, os seus mecanismos de defesa e as suas falhas. De tempos a tempos surgem notícias da falha A ou B. Temos que nos manter atualizados e ver as implicações das mesmas nas nossas aplicações.
O uso da keystore é uma boa solução, mas com restrições. Por exemplo, o keystore fica indisponível quando o equipamento se encontra bloqueado e requer autenticação do utilizador, por código ou por impressão digital – pequeno detalhe que precisamos de relembrar quando desenhamos um serviço. No entanto, combinando os vários pontos que vimos até agora, conseguimos ter uma segurança aceitável, que levará muito mais do que 15 minutos a piratear.