O Android inclui recursos de segurança integrados que reduzem significativamente a frequência e o impacto dos problemas de segurança de aplicativos. O sistema é projetado para que você possa criar seus apps normalmente com permissões padrão do sistema e de arquivos além de evitar decisões difíceis sobre a segurança.
Os principais recursos de segurança a seguir ajudam você a desenvolver apps seguros:
- O sandbox de aplicativos para Android, que isola os dados e a execução do código do seu app de outros apps.
- Um framework de aplicativos com implementações robustas de funcionalidade comum de segurança, como criptografia, permissões e IPC (na sigla em inglês) segura.
- Tecnologias como ASLR, NX, ProPolice, safe_iop, OpenBSD dlmalloc, OpenBSD calloc e Linux mmap_min_addr para reduzir os riscos associados a erros comuns de gerenciamento de memória.
- Um sistema de arquivos criptografado que pode ser ativado para proteger os dados em dispositivos perdidos ou roubados.
- Permissões concedidas pelo usuário para restringir o acesso aos recursos do sistema e aos dados do usuário.
- Permissões definidas por aplicativos para controlar os dados de cada aplicativo individualmente.
É importante se familiarizar com as práticas recomendadas de segurança do Android apresentadas neste documento. Seguir estas práticas como hábitos gerais de criação de código reduzirá a probabilidade da introdução acidental de problemas de segurança que afetem seus usuários de maneira negativa.
Armazenamento de dados
A preocupação de segurança mais comum para um aplicativo Android é se os dados salvos no dispositivo podem ser acessados por outros apps. Existem três maneiras fundamentais de salvar dados no dispositivo:
- Armazenamento interno
- Armazenamento externo
- Provedores de conteúdo
Usar o armazenamento interno
Por padrão, os arquivos criados no armazenamento interno podem ser acessados apenas pelo seu app. O Android implementa essa proteção e é suficiente para a maioria dos aplicativos.
De forma geral, evite os modos MODE_WORLD_WRITEABLE
ou MODE_WORLD_READABLE
para arquivos IPC, porque eles não permitem limitar o acesso de dados a apps específicos nem oferecem controle sobre o formato dos dados. Se quiser compartilhar dados com outros processos de apps, use um provedor de conteúdo que ofereça permissões de leitura e gravação para outros apps e possa fazer concessões de permissões dinâmicas de acordo com cada caso.
Para fornecer mais proteção a dados confidenciais, você pode criptografar arquivos locais com a biblioteca de segurança. Essa medida pode fornecer proteção para um dispositivo perdido sem a criptografia do sistema de arquivos.
Usar o armazenamento externo
Arquivos criados no armazenamento externo, como cartões SD, podem ser lidos e gravados globalmente. Como o armazenamento externo pode ser removido pelo usuário e também modificado por qualquer app, não armazene informações confidenciais nele.
Para ler e gravar arquivos no armazenamento externo de maneira mais segura, use a biblioteca de segurança, que fornece a classe EncryptedFile
.
Você precisa validar as entradas ao processar dados de armazenamento externo como faria com dados de fontes não confiáveis. Não armazene arquivos executáveis ou de classe no armazenamento externo antes do carregamento dinâmico. Se o app recuperar arquivos executáveis de um armazenamento externo, eles precisam ser assinados e verificados criptograficamente antes do carregamento dinâmico.
Usar provedores de conteúdo
Provedores de conteúdo oferecem um mecanismo de armazenamento estruturado que pode ser limitado ao seu aplicativo ou exportado para permitir o acesso de outros aplicativos.
Se você não pretende fornecer a outros apps acesso ao seu ContentProvider
, marque-os como
android:exported=false
no manifesto do aplicativo. Caso contrário, defina o atributo android:exported
como true
para permitir que outros apps acessem os dados armazenados.
Ao criar um ContentProvider
que será exportado para o uso de outros apps, você pode especificar uma única permissão de leitura e gravação ou permissões distintas de leitura e gravação.
Limite suas permissões àquelas necessárias para realizar a tarefa em questão. Lembre-se de que geralmente é mais fácil adicionar permissões posteriormente para expor novos recursos do que removê-las e afetar os usuários já existentes.
Se você estiver usando um provedor de conteúdo para compartilhar dados apenas entre seus próprios apps, é preferível usar o conjunto de atributos android:protectionLevel
definido para a proteção signature
.
Permissões de assinatura não exigem a confirmação do usuário. Portanto, elas proporcionam uma experiência melhor para o usuário e um acesso mais controlado aos dados do provedor de conteúdo quando os apps que acessam os dados são assinados com a mesma chave.
Provedores de conteúdo também podem fornecer um acesso mais granular ao declarar o atributo android:grantUriPermissions
e usar os sinalizadores FLAG_GRANT_READ_URI_PERMISSION
e FLAG_GRANT_WRITE_URI_PERMISSION
no objeto Intent
que ativa o componente. O escopo dessas permissões pode ser mais limitado pelo elemento
<grant-uri-permission>
.
Ao acessar o provedor de conteúdo, use métodos parametrizados de consulta, como query()
, update()
e delete()
para evitar possíveis injeções de SQL de fontes não confiáveis. Observe que o uso de métodos parametrizados não é suficiente se o argumento selection
foi criado por meio da concatenação de dados do usuário antes de enviá-los ao método.
Não crie uma falsa sensação de segurança sobre a permissão de gravação.
A permissão de gravação permite instruções SQL que possibilitam que alguns dados sejam confirmados usando cláusulas WHERE
criativas e analisando os resultados. Por exemplo, um invasor pode verificar a presença de um número de telefone específico em um registro de chamadas, modificando uma linha apenas se esse número já existir. Se os dados do provedor de conteúdo tiverem uma estrutura previsível, a permissão de gravação pode equivaler a uma permissão de leitura e gravação.
Usar permissões
Como o Android coloca os aplicativos uns dos outros no sandbox, eles precisam compartilhar recursos e dados de forma explícita. Isso é feito ao declarar as permissões necessárias para outros recursos não fornecidos pelo sandbox básico, incluindo o acesso a recursos do dispositivo, como a câmera.
Solicite permissões
É preciso minimizar o número de permissões solicitadas pelo app.
Restringir o acesso a permissões confidenciais reduz o risco do uso indevido acidental dessas permissões, melhora a adoção dos usuários e torna seu app menos vulnerável a invasores. Em geral, se uma permissão não é necessária para o funcionamento do seu app, não é preciso solicitá-la.
Se um recurso for indispensável para o app, declare isso usando um elemento <uses-feature>
no arquivo de manifesto.
Se for possível projetar seu app de forma que não exija permissões, essa é a abordagem preferencial. Por exemplo, em vez de solicitar o acesso a informações do dispositivo para criar um identificador exclusivo, crie um GUID para seu aplicativo (consulte a seção sobre Processar dados do usuário). Ou então, em vez de usar um armazenamento externo (que exige permissão), coloque dados no armazenamento interno.
Além de solicitar permissões, seu aplicativo pode usar o elemento <permission>
para proteger a IPC, que é sensível à segurança e é exposta a outros apps, como um ContentProvider
.
Em geral, recomendamos o uso de controles de acesso, além de permissões confirmadas pelo usuário sempre que possível, porque as permissões podem ser confusas para os usuários. Por exemplo, considere o uso do nível de proteção de assinatura em permissões para comunicação de IPC entre apps fornecidos por um só desenvolvedor.
Não vaze dados protegidos por permissões. Isso ocorre quando seu app expõe dados pela IPC que está disponível apenas porque ele tem permissão para acessar esses dados. Os clientes da interface IPC do seu app podem não ter as mesmas permissões de acesso a dados. Mais detalhes sobre a frequência e os possíveis efeitos desse problema aparecem no trabalho de pesquisa Re-Delegação de permissão: ataques e defesas (link em inglês), publicado na USENIX.
Criar permissões
Em geral, tente definir o menor número possível de permissões e atender aos seus requisitos de segurança. Criar uma nova permissão é relativamente incomum para a maioria dos aplicativos, porque as permissões definidas pelo sistema englobam muitas situações. Quando apropriado, realize verificações de acesso usando permissões já existentes.
Se for preciso criar uma nova permissão, considere se sua tarefa pode ser executada com um nível de proteção de assinatura. Permissões de assinatura são transparentes para o usuário e só permitem o acesso de apps assinados pelo mesmo desenvolvedor do app que executa a verificação de permissão. Se a nova permissão ainda for necessária, ela será declarada no manifesto do app usando o elemento <permission>
. Os apps que quiserem usar a nova permissão poderão referenciá-la ao adicionar um elemento <uses-permission>
nos respectivos arquivos de manifesto. Também é possível adicionar permissões de modo dinâmico usando o método addPermission()
.
Se você criar uma permissão com o nível de proteção "perigoso", considere as seguintes complexidades:
- A permissão precisa ter uma string que expresse, de forma concisa, a decisão de segurança que o usuário precisa tomar.
- A string de permissão precisa ser localizada para vários idiomas diferentes.
- Os usuários podem escolher não instalar um aplicativo porque uma permissão parece ser confusa ou arriscada.
- Aplicativos podem solicitar a permissão quando o criador dela não tiver sido instalado.
Cada uma dessas situações apresenta um desafio não técnico significativo para você, como desenvolvedor, além de confundir seus usuários. Por esse motivo, não incentivamos o uso do nível de permissão perigoso.
Usar rede
Transações de rede são inerentemente arriscadas para segurança, porque envolvem a transmissão de dados potencialmente privados para o usuário. As pessoas estão cada vez mais conscientes das questões de privacidade de um dispositivo móvel, especialmente quando esse dispositivo executa transações de rede. Dessa forma, é muito importante que seu app implemente todas as práticas recomendadas para manter os dados do usuário protegidos a todo o momento.
Usar rede IP
A rede do Android não é muito diferente de outros ambientes Linux. A principal consideração é garantir que os protocolos apropriados sejam usados para dados confidenciais, como o HttpsURLConnection
para o tráfego seguro na Web. É recomendável usar HTTPS em vez de HTTP em qualquer local em que esse protocolo seja compatível com o servidor, porque os dispositivos móveis frequentemente se conectam a redes que não são protegidas, como pontos de acesso Wi-Fi públicos.
A comunicação no nível do soquete criptografada e autenticada pode ser facilmente implementada usando a classe SSLSocket
. Considerando a frequência com que os dispositivos Android se conectam a redes sem fio desprotegidas usando Wi-Fi, o uso da rede segura é fortemente encorajado para todos os aplicativos que se comunicam por rede.
Alguns apps usam as portas de rede localhost para processar uma IPC confidencial. Não recomendamos o uso dessa abordagem, porque essas interfaces podem ser acessadas por outros aplicativos do dispositivo. Em vez disso, use um mecanismo de IPC do Android em que a autenticação seja possível, como um Service
.
Vincular a INADDR_ANY é pior do que usar um loopback, porque seu aplicativo poderá receber solicitações de qualquer lugar.
Não confie em dados transferidos por download de HTTP ou de outros protocolos não seguros. Isso inclui a validação de entradas no WebView
e de qualquer resposta a intents emitidas por HTTP.
Usar rede de telefonia
O protocolo SMS foi projetado principalmente para a comunicação entre usuários e não é adequado para apps que querem transferir dados. Devido às limitações do SMS, é recomendado o uso do Google Cloud Messaging (GCM) e da rede IP para o envio de mensagens de dados de um servidor da Web para seu app em um dispositivo do usuário.
Esteja ciente de que o SMS não é criptografado ou fortemente autenticado na rede ou no dispositivo. Em particular, qualquer receptor de destinatário de SMS precisa prever que um usuário mal-intencionado possa ter enviado o SMS para seu aplicativo. Não utilize dados SMS não autenticados para executar comandos essenciais.
Além disso, esteja ciente de que o SMS pode estar sujeito a spoofing e/ou a interceptação na rede. No próprio dispositivo Android, as mensagens SMS são transmitidas como intents de transmissão, portanto, elas podem ser lidas ou capturadas por outros aplicativos que tenham a permissão READ_SMS
.
Validar entradas
A validação insuficiente de entradas é um dos problemas de segurança mais comuns que afeta os aplicativos, independente da plataforma em que são executados. O Android tem medidas no nível da plataforma para reduzir a exposição de aplicativos a problemas de validação de entradas, e você precisa usar esses recursos sempre que possível. Observe também que a seleção de linguagens com segurança de tipo tende a reduzir a probabilidade de problemas de validação de entradas.
Se você estiver usando o código nativo, todos os dados lidos de arquivos, recebidos pela rede ou recebidos de um IPC podem introduzir um problema de segurança. Os problemas mais comuns são buffer excedente, uso após livre e erros off-by-one (em inglês). O Android oferece várias tecnologias, como ASLR e DEP, que reduzem a capacidade de exploração desses erros, mas não resolvem o problema subjacente. Essas vulnerabilidades podem ser evitadas com o processamento cuidadoso de ponteiros e o gerenciamento de buffers.
Linguagens dinâmicas baseadas em strings, como JavaScript e SQL, também estão sujeitas a problemas de validação de entradas devido a caracteres com escape e à injeção de script.
Se você estiver usando dados em consultas enviadas a um banco de dados SQL ou um provedor de conteúdo, a injeção de SQL poderá ser um problema. A melhor defesa é o uso de consultas parametrizadas, conforme discutido na seção acima sobre provedores de conteúdo. A limitação de permissões para apenas leitura ou apenas gravação também pode reduzir o risco relacionado à injeção de SQL.
Se você não puder usar os recursos de segurança acima, use formatos de dados bem estruturados e verifique se os dados seguem o formato esperado. Embora a inclusão de caracteres na lista de proibições ou a substituição de caracteres possam ser estratégias eficazes, essas técnicas, na prática, tendem ao erro e precisam ser evitadas sempre que possível.
Processar dados do usuário
Em geral, a melhor abordagem para a segurança dos dados do usuário é minimizar o uso de APIs que acessam dados confidenciais ou pessoais. Se você tiver acesso aos dados do usuário e puder evitar o armazenamento ou a transmissão dessas informações, faça isso. Considere se existe uma maneira de implementar a lógica do seu aplicativo usando um hash ou um formato não reversivo dos dados. Por exemplo, seu aplicativo pode usar o hash de um endereço de e-mail como uma chave primária para evitar a transmissão ou o armazenamento do endereço de e-mail. Isso reduz as chances de exposição acidental dos dados e de tentativas de exploração do seu aplicativo por parte de invasores.
Caso seu aplicativo acesse informações pessoais como senhas ou nomes de usuário, lembre-se de que algumas jurisdições podem exigir que você forneça uma política de privacidade explicando seu uso e armazenamento desses dados. Portanto, o uso das práticas recomendadas de segurança para minimizar o acesso aos dados do usuário também pode simplificar o processo de compliance.
Considere também se o aplicativo pode estar expondo acidentalmente informações pessoais para outras partes, como componentes de terceiros para publicidade ou serviços de terceiros usados por seu aplicativo. Se não souber porque um componente ou serviço precisa de informações pessoais, não as forneça. Em geral, a redução do acesso a informações pessoais por parte do seu app reduzirá a probabilidade de problemas nessa área.
Se o acesso a dados confidenciais for necessário, avalie se essas informações precisam ser transmitidas a um servidor ou se a operação pode ser executada no cliente. Considere executar qualquer código usando dados confidenciais no cliente para evitar a transmissão de dados do usuário. Além disso, evite ao máximo a exposição acidental de dados do usuário a outros aplicativos do dispositivo com um IPC muito permissivo, arquivos graváveis globalmente ou soquetes de rede. A IPC excessivamente permissiva é um caso especial de vazamento de dados protegidos por permissão, discutido na seção Solicitação de permissões.
Se um GUID for necessário, crie um número grande e exclusivo e armazene-o. Não use identificadores de telefone, como o número de telefone ou o IMEI, que podem ser associados a informações pessoais. Esse tópico é discutido em mais detalhes no Blog de desenvolvedores Android.
Tenha cuidado ao programar em registros no dispositivo.
No Android, os registros são um recurso compartilhado e são disponibilizados para um aplicativo com a permissão READ_LOGS
.
Mesmo que os dados de registro do telefone sejam temporários e apagados após a reinicialização, o registro inapropriado de informações do usuário pode acabar vazando dados do usuário para outros apps. Além de não registrar PII, os apps de produção precisam limitar o uso de registro. Para implementá-lo com facilidade, use sinalizações de depuração e classes de Log
personalizadas com níveis de registro facilmente configuráveis.
Usar WebView
Como o WebView
consome conteúdo da Web que pode incluir HTML e JavaScript, o uso impróprio pode introduzir problemas comuns de segurança da Web, por exemplo, Scripting em vários locais (injeção de JavaScript). O Android inclui diversos mecanismos para reduzir o escopo desses possíveis problemas limitando a capacidade do WebView
à funcionalidade mínima necessária para seu app.
Caso seu app não use JavaScript diretamente em WebView
, não chame setJavaScriptEnabled()
.
Alguns códigos de exemplo usam esse método, que pode ser reutilizado no app de produção. Portanto, remova essa chamada do método se ela não for necessária. Por padrão, o WebView
não executa JavaScript, o que impossibilita o Scripting em vários locais.
Use addJavaScriptInterface()
com cuidado, porque ele permite que o JavaScript invoque operações normalmente reservadas para aplicativos Android. Se você usá-lo, exponha addJavaScriptInterface()
apenas para páginas da Web em que todas as entradas sejam confiáveis. Se a entrada não confiável for permitida, JavaScript não confiável poderá invocar métodos do Android no seu app. Em geral, recomendamos a exposição de addJavaScriptInterface()
apenas ao JavaScript contido no APK do app.
Caso seu app acesse dados confidenciais com um WebView
, é uma boa ideia usar o método clearCache()
para excluir arquivos armazenados localmente. Você também pode usar cabeçalhos do lado do servidor, como o no-cache
, para indicar que um app não pode armazenar em cache conteúdos específicos.
Dispositivos que executam plataformas mais antigas que o Android 4.4 (API de nível 19) usam uma versão do webkit
que tem diversos problemas de segurança.
Como solução alternativa, caso seu app estiver sendo executado nesses dispositivos, ele precisará confirmar que os objetos WebView
exibem apenas conteúdo confiável. Para garantir que seu app não seja exposto a possíveis vulnerabilidades no SSL, use o objeto de segurança atualizável Provider
, conforme descrito em Como atualizar seu provedor de segurança para se proteger contra explorações de SSL. Caso seu app precise renderizar conteúdo da Web aberta, disponibilize o próprio renderizador para mantê-lo atualizado com os patches de segurança mais recentes.
Processar credenciais
Para tornar os ataques de phishing mais evidentes e reduzir a probabilidade de sucesso deles, minimize a frequência de solicitações de credenciais do usuário. Em vez disso, use um token de autorização e atualize-o.
Sempre que possível, não armazene nomes de usuário e senhas no dispositivo. Em vez disso, realize a autenticação inicial usando o nome de usuário e a senha fornecidos pelo usuário e, depois, use um token de autorização de curta duração específico para serviços.
Serviços acessíveis a vários apps precisam ser acessados usando AccountManager
. Se possível, use a classe AccountManager
para invocar um serviço baseado na nuvem e não armazene senhas no dispositivo.
Depois de usar AccountManager
para recuperar Account
, use CREATOR
antes de enviar as credenciais para não transmiti-las de maneira não intencional para o app errado.
Se as credenciais são usadas apenas por aplicativos criados por você, é possível verificar o aplicativo que acessar o AccountManager
usando checkSignature()
.
Como alternativa, se apenas um app usar a credencial, use um KeyStore
para armazenamento.
Usar criptografia
Além de oferecer isolamento de dados, compatibilidade com a criptografia para todo o sistema de arquivos e canais de comunicação seguros, o Android fornece diversos algoritmos para proteger dados usando a criptografia.
Em geral, você precisa saber quais provedores de segurança da Arquitetura de criptografia Java (JCA, na sigla em inglês) seu software usa. Tente usar o nível mais alto de implementação da estrutura pré-existente que seja compatível com seu caso de uso. Se aplicável, use os provedores fornecidos pelo Google no pedido especificado por ele.
Para ler e gravar arquivos locais com mais segurança, use a Biblioteca de segurança.
Se você precisa recuperar um arquivo de um local conhecido de forma segura, um simples URI HTTPS pode ser adequado e não exige conhecimento de criptografia. Se precisar de um túnel seguro, considere usar HttpsURLConnection
ou SSLSocket
, em vez de criar seu próprio protocolo. Se você usa o SSLSocket, esteja ciente de que ele não executa a verificação do nome do host. Consulte Avisos sobre o uso direto do SSLSocket.
Se precisar implementar seu próprio protocolo, não implemente seus próprios algoritmos criptográficos. Use algoritmos criptográficos já existentes, por exemplo, as implementações de AES e RSA disponibilizadas na classe Cipher
. Além disso, siga estas práticas recomendadas:
- Use AES de 256 bits para fins comerciais. Se ele não estiver disponível, use o AES de 128 bits.
- Use chaves públicas de 224 ou 256 bits para criptografia de curva elíptica (EC, na sigla em inglês).
- Saiba quando usar os modos de bloqueio CBC, CTR ou GCM.
- Evite reutilizar o vetor de inicialização (IV, na sigla em inglês)/contador no modo CTR. Eles precisam ser criptograficamente aleatórios.
- Ao usar criptografia, implemente a integridade usando o modo CBC ou CTR com uma das seguintes funções:
- HMAC-SHA1
- HMAC-SHA-256
- HMAC-SHA-512
- Modo GCM
Use um gerador seguro de números aleatórios, SecureRandom
, para inicializar qualquer chave criptográfica gerada por KeyGenerator
.
O uso de uma chave que não foi criada por um gerador de número aleatório seguro reduz significativamente a força do algoritmo e pode permitir ataques off-line.
Se você precisar armazenar uma chave para uso repetido, use um mecanismo como KeyStore
, que oferece armazenamento de longo prazo e recuperação de chaves criptográficas.
Usar comunicação entre processos
Alguns apps tentam implementar IPC usando técnicas tradicionais do Linux, como soquetes de rede e arquivos compartilhados. No entanto, você precisa usar a funcionalidade do sistema Android para IPC, por exemplo, Intent
, Binder
ou Messenger
com Service
e BroadcastReceiver
.
Os mecanismos de IPC do Android permitem verificar a identidade do app que estiver se conectando à sua IPC e definir políticas de segurança para cada mecanismo de IPC.
Muitos dos elementos de segurança são compartilhados entre mecanismos de IPC.
Caso seu mecanismo de IPC não se destine ao uso por outros apps, defina o atributo android:exported
como false
no elemento de manifesto do componente, por exemplo, para o elemento <service>
. Isso é útil para apps que consistem em vários processos no mesmo UID ou se você decidir mais tarde durante o desenvolvimento que não quer expor a funcionalidade como IPC, mas não quiser reprogramar o código.
Se sua IPC estiver acessível a outros apps, você poderá aplicar uma política de segurança usando o elemento <permission>
. Se a IPC estiver entre seus apps diferentes que foram assinados com a mesma chave, é preferível usar a permissão de nível signature
no android:protectionLevel
.
Usar intents
Para atividades e broadcast receivers, as intents são o mecanismo mais recomendado para IPC assíncrona no Android.
Dependendo dos requisitos do seu app, você pode usar sendBroadcast()
, sendOrderedBroadcast()
ou uma intent explícita para um componente de aplicativo específico. Por motivos de segurança, intents explícitas são preferidas.
Cuidado: se você usar uma intent para vincular um Service
, use uma intent explícita para verificar se o app é seguro. O uso de uma intent implícita para iniciar um serviço representa um risco de segurança, porque não é possível determinar qual serviço responderá à intent, e o usuário não poderá ver qual serviço será iniciado. A partir do Android 5.0 (API de nível 21), o sistema gera uma exceção se você chama bindService()
com uma intent implícita.
Observe que transmissões ordenadas podem ser consumidas por um destinatário. Portanto, elas podem não ser entregues para todos os apps. Se você estiver enviando uma intent que precisa ser entregue para um destinatário específico, use uma intent explícita que declare o destinatário pelo nome.
Os remetentes de uma intent podem verificar se o destinatário tem uma permissão que especifique uma permissão não nula com a chamada do método. Apenas aplicativos com essa permissão receberão a intent. Se for possível que os dados em uma intent de transmissão sejam confidenciais, considere aplicar uma permissão para garantir que aplicativos mal-intencionados não possam se registrar para receber essas mensagens sem as permissões adequadas. Nessas condições, considere também invocar o destinatário diretamente, em vez de gerar uma transmissão.
Observação: os filtros de intent não podem ser considerados um recurso de segurança. Componentes podem ser invocados com intents explícitas e podem não ter dados em conformidade com o filtro de intent. Para confirmar se ele está formatado de maneira correta para o destinatário, o serviço ou a atividade que foi invocada, execute a validação de entradas no seu destinatário de intent.
Usar serviços
Um Service
é frequentemente usado para fornecer recursos para o uso por outros apps. Cada classe de serviço precisa ter uma declaração <service>
correspondente no arquivo de manifesto.
Por padrão, os serviços não são exportados e não podem ser invocados por qualquer outro app. No entanto, se você adicionar filtros de intent na declaração de serviço, ele será exportado por padrão. É recomendável declarar o atributo android:exported
de forma explícita para garantir que ele se comporte da forma visada.
Os serviços também podem ser protegidos usando o atributo android:permission
. Ao fazer isso, outros apps precisam declarar um elemento <uses-permission>
correspondente no próprio manifesto para poder iniciar, parar ou se vincular ao serviço.
Observação: caso seu app seja direcionado para o Android 5.0 (API de nível 21) ou versões mais recentes, use o JobScheduler
para executar serviços em segundo plano. Para saber mais sobre JobScheduler
, consulte a API-reference documentation
.
Um serviço pode proteger chamadas de IPC individuais dentro dele com permissões ao chamar checkCallingPermission()
antes de executar a implementação dessa chamada. Use as permissões declarativas no manifesto, porque elas são menos propensas a lapsos.
Atenção: não confunda permissões de cliente e servidor. Verifique se o app chamado tem as permissões adequadas e verifique se você concede as mesmas permissões ao app de chamada.
Usar interfaces binder e messenger
O uso de Binder
ou Messenger
é o mecanismo preferencial para a IPC de estilo RPC no Android. Eles proporcionam uma interface bem definida que possibilita a autenticação mútua dos endpoints, se necessário.
Projete suas interfaces de app de forma que elas não exijam verificações de permissões específicas de interface. Os objetos Binder
e Messenger
não são declarados no manifesto do app. Por isso, não é possível aplicar permissões declarativas diretamente neles. Em geral, eles herdam permissões declaradas no manifesto do app para o Service
ou a Activity
na qual são implementados. Se você está criando uma interface que exige autenticação e/ou controles de acesso, é preciso adicionar esses controles explicitamente como código na interface Binder
ou Messenger
.
Se você está disponibilizando uma interface que exige controles de acesso, use checkCallingPermission()
para verificar se o autor da chamada tem uma permissão obrigatória. Isso é especialmente importante antes de acessar um serviço em nome do autor da chamada, porque a identidade do seu app é transmitida para outras interfaces. Se você estiver invocando uma interface fornecida por um Service
, a invocação de bindService()
poderá falhar se você não tiver permissão para acessar o serviço fornecido.
Se você está chamando uma interface fornecida localmente pelo seu app, é interessante usar o método clearCallingIdentity()
, que mascara as permissões do autor da chamada em relação às permissões do app, a fim de satisfazer às verificações internas de segurança. É possível restaurar as permissões do autor da chamada mais tarde usando o método restoreCallingIdentity()
.
Para mais informações sobre como executar IPC com um serviço, consulte Serviços vinculados.
Usar broadcast receivers
Um BroadcastReceiver
gerencia as solicitações assíncronas iniciadas por uma Intent
.
Por padrão, os receptores são exportados e podem ser invocados por qualquer outro aplicativo. Se o BroadcastReceiver
for destinado ao uso por outros apps, convém aplicar permissões de segurança a receptores usando o elemento
<receiver>
no manifesto do app. Isso evita que os apps que não têm as permissões necessárias enviem uma intent ao BroadcastReceiver
.
Carregar código dinamicamente
Não recomendamos o carregamento de código fora do APK do seu app. Isso pode aumentar de maneira significativa a probabilidade de comprometimento do app por injeção ou violação de código. Essa abordagem também adiciona complexidade ao gerenciamento de versões e ao teste de apps. Por fim, ela também pode impossibilitar a verificação do comportamento de um aplicativo. Portanto, pode ser proibida em alguns ambientes.
Se o aplicativo carregar código de forma dinâmica, o mais importante a se lembrar é que ele é executado com as mesmas permissões de segurança que o APK do aplicativo. O usuário tomou a decisão de instalar seu aplicativo com base na sua identidade e espera que todas as execuções de códigos sejam realizadas no aplicativo, inclusive códigos carregados dinamicamente.
O principal risco de segurança associado ao carregamento dinâmico é que o código precisa ser originado em uma fonte verificável. Se os módulos forem incluídos diretamente no seu APK, eles não poderão ser modificados por outros apps.
Isso é válido independentemente do código ser uma biblioteca nativa ou uma classe carregada usando DexClassLoader
. Muitos apps tentam carregar códigos de locais inseguros, por exemplo, códigos transferidos por download da rede por meio de protocolos não criptografados ou de locais globalmente graváveis, como um armazenamento externo. Esses locais poderiam permitir que um usuário da rede modificasse o conteúdo em trânsito ou que outro aplicativo no dispositivo de um usuário modificasse o conteúdo no dispositivo.
Segurança em uma máquina virtual
O Dalvik é a VM (máquina virtual) de tempo de execução do Android. O Dalvik foi criado de forma específica para o Android, mas muitas das preocupações relacionadas com a segurança do código em outras máquinas virtuais também se aplicam ao Android. Em geral, não é necessário se preocupar com problemas de segurança relacionados à máquina virtual. Seu aplicativo é executado em um ambiente de sandbox seguro, portanto, outros processos no sistema não podem acessar seu código ou dados privados.
Se tiver interesse em saber mais sobre segurança de máquinas virtuais, familiarize-se com o material existente sobre o assunto. Dois dos recursos mais populares são:
Este documento se concentra em áreas específicas do Android ou que diferem de outros ambientes de VM. Para desenvolvedores que têm experiência em programação de VMs em outros ambientes, existem duas questões amplas que podem ser diferentes na programação de apps para Android:
- Algumas máquinas virtuais, como a JVM ou o tempo de execução .net, funcionam como limites de segurança, isolando o código dos recursos subjacentes do sistema operacional. No Android, a VM Dalvik não é um limite de segurança. O sandbox do aplicativo é implementado no nível do SO, assim o Dalvik pode ser interoperado com o código nativo no mesmo aplicativo, sem limitações de segurança.
- Considerando o armazenamento limitado dos dispositivos móveis, é comum que os desenvolvedores prefiram criar aplicativos modulares e usar o carregamento dinâmico de classes. Ao fazer isso, considere a fonte em que você recupera a lógica do seu aplicativo e onde você a armazena localmente. Não use o carregamento dinâmico de classes de fontes que não sejam verificadas, como fontes de redes não protegidas ou armazenamentos externos, porque o código pode ser modificado para incluir comportamentos mal-intencionados.
Segurança no código nativo
Em geral, recomendamos o uso do Android SDK para o desenvolvimento de apps, em vez de usar o código nativo com o Android NDK. Apps criados com código nativo são mais complexos, menos portáteis e mais propensos a incluir erros comuns de corrupção de memória, por exemplo, sobrecargas de buffer.
O Android é criado com o kernel do Linux, e estar familiarizado com as práticas recomendadas para a segurança de desenvolvimento do Linux é especialmente útil se você estiver usando código nativo. As práticas de segurança do Linux estão fora do escopo deste documento, mas um dos recursos mais usados é Secure Programming HOWTO - Creating Secure Software (link em inglês).
Uma diferença importante entre o Android e a maioria dos ambientes Linux é o sandbox de aplicativos. No Android, todos os aplicativos são executados no sandbox de aplicativos, incluindo os criados com código nativo. No nível mais básico, uma boa maneira de considerar esse cenário para desenvolvedores com experiência em Linux é saber que cada app recebe um UID exclusivo com permissões muito limitadas. Isso é discutido em mais detalhes na Visão geral de segurança do Android. Familiarize-se com as permissões do app, mesmo que esteja usando código nativo.