Os modelos de segurança do Android
Que o Android é um dos sistemas operacionais mais utilizados hoje em dia nós já sabemos, mas você sabia que ele também oferece diversos mecanismos de segurança para garantir que os dados do usuário estejam o mais protegidos possível?
Muitos desses mecanismos são herdados do kernel no qual o Android se baseia: Linux; outros, foram concebidos logo no início do planejamento da plataforma. A seguir vou listar cada um desses mecanismos, discutir um pouco o porquê de protegerem o usuário, bem como montar um paralelo com outras plataformas, tais como computadores pessoais.
O processo de boot e Rollback Prevention
A segurança do Android já começa a partir da primeira vez que o dispositivo é ligado pelo usuário, em que toda a cadeia de boot, o processo de ligar o telefone, no qual diversos serviços são inicializados, é verificada para garantir a integridade do software a ser executado, porém ainda deixa a cargo do usuário decidir se permitiria que uma versão modificada do Android fosse instalada no seu dispositivo, dando maior liberdade aos usuários. Vale lembrar que a opção de deixar liberada a escolha para o usuário fica a cargo do fabricante do dispositivo.
Cada fabricante de celulares Android (Google, Samsung, Motorola etc.) possuí uma chave criptográfica que é utilizada para assinar o software que será distribuído nos seus telefones. Um dispositivo da respectiva marca só aceita um update de versão do Android caso seja possível validar que essa assinatura é, de fato, da fabricante. Isso é o chamado root of trust.
Além disso, para cada nova versão instalada no dispositivo é mantido o valor numérico dessa versão salvo numa área que existe antes de qualquer processo de boot, possibilitando ao próprio dispositivo nega a instalação de uma versão abaixo da que está presente no momento. Isso é o chamado rollback prevention[2].
Mas por que isso protege o usuário?
Imagine um cenário em que foi descoberta uma vulnerabilidade de escalada de privilégio no kernel Linux, ou seja, um atacante, em posse de um dispositivo vulnerável, poderia ter acesso ao usuário root, o usuário superpoderoso no Linux.
Contudo, o dispositivo em posse do atacante contém uma versão muito recente do Android. Uma alternativa seria ele instalar aquela versão vulnerável que falamos a pouco, porém o rollback prevention vai garantir que isso não aconteça.
Vale lembrar que o usuário pode abrir mão dessas proteções para instalar sistemas operacionais customizados.
Binder
Para entender o que é o Binder e porque ele é uma peça chave na segurança de qualquer dispositivo Android, primeiro precisamos entender como os primeiros processos do Android são criados e segregados entre si.
Em algum momento do processo de boot o kernel Linux será inicializado, da mesma maneira que em computadores pessoais, no qual o arquivo de inicialização init.rc dará as informações necessárias d para que o processo de boot ocorra corretamente. Um desses processos é a inicialização do Android propriamente dito, o chamado framework, que pode ser entendido como sendo o processo do system_server, em que conterá a maior parte dos serviços essenciais para que o telefone funcione.
Esse processo é muito privilegiado no ambiente Android, sendo responsável pelas mais diversas funcionalidades, desde permitir acesso a certos diretórios, instalar/desinstalar novos aplicativos, até mesmo alterar configurações do sistema e muito mais. Ele também é o processo que pode entrar em contato com o kernel, seja para requisitar uma exceção ou realizar alguma validação, coisa que processos de usuário (como aplicações) não tem permissão.
Porém, e quando uma aplicação precisa abrir um arquivo, por exemplo?
No Linux, para lidar com arquivo, é necessário pedir ao Kernel que prepare uma região de memória para leitura/escrita onde esse arquivo será carregado. O Kernel fica responsável por fazer essa preparação e retornar um objeto que identifica o arquivo para o processo que quer usá-lo.
Já no Android, é necessário passar todos esses pedidos ao system_server e este é quem vai se comunicar com o Kernel. O mesmo vale caso uma aplicação A queira acessar algo da aplicação B, todo pedido é feito através do system_server, que por sua vez carrega a informação de quem está o requisitando para o kernel validar se a aplicação A tem permissão e enfim realizar a requisição. Esse processo é o chamado IPC (inter-process communication).
Para auxiliar nessa jornada de idas e vindas foi criado o Binder, uma estrutura responsável por associar cada pedido a um aplicativo. Dessa maneira é possível sempre saber quem é o requisitante de uma ação e a quem o resultado deve ser devolvido, garantindo que um aplicativo malicioso não possa se passar por um aplicativo legítimo para acessar algo que não deve.
Segregação de Aplicações
No Android, o contexto de sandbox de aplicação é muito forte. Isso nada mais é do que prender cada aplicação na sua devida caixinha de areia e, caso uma caixinha precise acessar um recurso de outra caixinha, é necessário pedir permissão ao system_server.
Cada aplicação que você tem instalada no seu dispositivo fica indisponível para o sistema até ser inicializada. Assim que for inicializada (seja através de você clicando no ícone na página inicial ou através de um link que a aplicação registra) o kernel atribui um identificador único a essa aplicação (seu uid) e ela se torna um processo vivo.
Nesse momento, no Android, um processo A não sabe nem da existência de um processo B, a caixa de areia é tão fechada que é necessário requisitar tudo através do sistema.
Mas, na prática, no que isso ajuda na segurança do usuário?
Vamos imaginar dois cenários: (I) acessar o internet banking do navegador de seu computador pessoal que tem sistema operacional Windows e; (II) acessar o aplicativo do banco no celular:
Em (I) o internet banking é executado dentro do contexto do navegador, porém não existe uma forte segregação entre processos no Windows. Caso o usuário tenha um malware, este conseguiria ler a memória, por completo, do processo do navegador, vendo todas as requisições feitas para o servidor e qualquer dado que esteja em claro na memória.
Em (II) o aplicativo do banco ficará “preso” dentro de sua caixinha de areia, ou seja, não poderá se comunicar diretamente com outros aplicativos, mas outros processos também não poderão se comunicar diretamente com ele. Isso garante um maior controle de acesso, uma vez que todo os pedidos de comunicação devem passar pelos serviços privilegiados do Android.
Permissões
Contudo, muitas vezes é necessário que uma aplicação acesse recursos do sistema, tais como o armazenamento externo (/sdcard) ou a câmera. Se cada aplicação fica presa na sua caixinha de areia, como isso é possível?
Novamente vamos recorrer pro system_server para garantir o acesso, porém, nesses casos será necessário entender o uso de permissões.
Permissões são Strings bem definidas no sistema Android que uma aplicação pode definir pela primeira vez ou declarar que a utiliza no seu AndroidManifest.xml [3], um tipo de arquivo que tem todas as informações relevantes às configurações do aplicativo.
Cada permissão tem um nível de proteção, sendo os mais comuns: normal, que qualquer aplicativo pode declarar que usa e o sistema irá garantir esse uso, dangerous, que é quando você abre o aplicativo e aparece uma telinha solicitando sua autorização para liberar o recurso e signature, que só pode ser concedida para aplicativos que possuem a mesma assinatura do aplicativo que declarou a permissão. Na próxima seção você entenderá melhor o que é essa assinatura e como ela funciona nesse contexto.
Dessa maneira, podemos dizer que um aplicativo que que nunca pediu sua permissão para utilizar o recurso, não conseguirá acesso à sua câmera, por exemplo do uso de câmera não conseguirá acesso à câmera do usuário, por exemplo.
Assinatura de aplicações
Para instalar um aplicativo é necessário que todo seu código esteja assinado com uma chave do desenvolvedor.
Na prática, cada empresa de desenvolvimento de aplicativos tem um conjunto de chaves criptográficas que são usadas para assinar cada arquivo de código, recurso e configuração de um aplicativo. Após essa assinatura, o aplicativo carrega meta-dados com um hash do resultado dessa assinatura para cada arquivo.
Isso é útil, pois evita o cenário em que um atacante construa um aplicativo malicioso com o mesmo “nome” (aqui é usado “nome” para designar o nome do pacote do aplicativo, um identificador único do aplicativo). Porém, como ele não terá acesso à chave que assinou o aplicativo original, não será possível gerar a mesma assinatura para o aplicativo. Ao tentar instalar essa versão maliciosa, caso o usuário já tenha uma versão original do aplicativo instalada, o sistema Android irá detectar e negar a instalação.
Atualizações recorrentes
Finalmente, mas não menos importante, é comum que os fabricantes de dispositivos Android forneçam atualizações, seja de versões do Android ou patches de segurança.
Nenhum sistema é 100% seguro e é comum que novas vulnerabilidades sejam descobertas no Android ou qualquer outro sistema operacional, porém muitos fabricantes garantem atualizações mensais do sistema para corrigir essas vulnerabilidades.
Enfim, agora podemos voltar no subtítulo deste blog post. Por que me sinto mais seguro utilizando o aplicativo do banco no meu telefone Android do que o internet banking no meu computador pessoal?
Vamos lembrar o que foi mencionado sobre computadores pessoais Windows, por exemplo: todos processos são executados num mesmo contexto, têm ciência de outros processos e podem ler/alterar a memória deles, ou seja, um malware no computador pessoal poderia ver a memória do processo que está executando o internet banking e ter acesso à suas informações. Ou seja, um malware no computador pessoal poderia ver a memória do processo que está executando o internet banking.
Enquanto isso, no Android, após analisar cuidadosamente cada um dos recursos de segurança descritos acima, podemos ver que, mesmo num cenário em que um usuário tenha um aplicativo malicioso instalado no celular, ele ainda teria proteções do sistema Android de que este malware está preso na sua caixinha de areia e não conseguiria se comunicar com o app do banco, a menos que o banco tenha implementado algum mecanismo para isso.
Por fim, os mecanismos de segurança do Android não se limitam a estes mencionados anteriormente, ainda existem outros mecanismos como: SELinux e Keystore, Trusted Execution Environment (TEE) que serão discutido em um próximo blogpost.
Referências
[1] https://source.android.com/security
[2] https://android.googlesource.com/platform/external/avb/+/master/README.md#Rollback-Protection
[3] https://developer.android.com/guide/topics/manifest/manifest-intro