Diretrizes de segurança

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 abaixo ajudam você a desenvolver apps seguros:

  • O sandbox de apps Android, que isola os dados e a execução do código do seu aplicativo de outros.
  • Um framework de aplicativos com implementações robustas de funcionalidades comuns de segurança, como criptografia, permissões e comunicação entre processos (IPC) segura.
  • Tecnologias como a ordem aleatória do layout do espaço de endereço (ASLR, na sigla em inglês), não executáveis (NX, na sigla em inglês), ProPolice, safe_iop, OpenBSD dlmalloc e calloc, e também mmap_min_addr no Linux para reduzir os riscos associados a erros comuns do gerenciamento de memória (links em inglês).
  • 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 nesta página. Siga as práticas como hábitos gerais de programação para evitar introduções acidentais de problemas de segurança que afetam negativamente seus usuários.

Autenticação

A autenticação é um pré-requisito para muitas operações de segurança importantes. Para controlar o acesso a recursos protegidos, como dados do usuário, funcionalidade do app e outros recursos, é necessário adicionar a autenticação ao app Android.

Você pode melhorar a experiência de autenticação do usuário integrando seu app à API Credential Manager. A API Credential Manager é uma biblioteca do Android Jetpack que unifica o suporte à API para a maioria dos principais métodos de autenticação, incluindo chaves de acesso, senhas e soluções de login federadas, como o recurso Fazer login com o Google.

Para melhorar ainda mais a segurança do app, adicione métodos de autenticação biométrica, como impressão digital ou reconhecimento facial. Bons candidatos para adicionar a autenticação biométrica incluem apps para gerenciamento de finanças, de saúde ou de identidade.

A Estrutura de preenchimento automático do Android facilita o processo de inscrição e login, reduzindo as taxas de erros e a dificuldade para o usuário. O preenchimento automático se integra a gerenciadores de senhas, permitindo que os usuários selecionem senhas complexas e aleatórias que podem ser armazenadas e extraídas com facilidade e segurança.

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

As seções abaixo descrevem os problemas de segurança associados a cada abordagem.

Armazenamento interno

Por padrão, os arquivos criados no armazenamento interno podem ser acessados apenas pelo seu app. O Android implementa essa proteção, que é suficiente para a maioria dos aplicativos.

Evite os modos descontinuados MODE_WORLD_WRITEABLEe MODE_WORLD_READABLE para arquivos com IPC. Não é possível usá-los para limitar o acesso a dados em aplicativos específicos nem ter controle sobre o formato dos dados. Se quiser compartilhar dados com outros processos de apps, use um provedor de conteúdo, que oferece permissões de leitura e gravação para outros apps e pode conceder permissões dinâmicas de acordo com cada caso.

Armazenamento externo

Arquivos criados no armazenamento externo, como cartões SD, podem ser lidos e gravados universalmente. Como o armazenamento externo pode ser removido pelo usuário e modificado por qualquer aplicativo, use-o para armazenar apenas informações não sensíveis.

Valide as entradas ao processar dados de armazenamento externo, da mesma forma que 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 extrair arquivos executáveis de um armazenamento externo, confira se eles foram assinados e verificados criptograficamente antes do carregamento dinâmico.

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 apps. Se você não pretende conceder acesso ao seu ContentProvider para outros aplicativos, marque-o como android:exported=false no manifesto do app. 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 às necessárias para realizar a tarefa em questão. Lembre-se de que geralmente é mais fácil adicionar permissões mais tarde para expor novos recursos do que removê-las e afetar os usuários atuais.

Se você estiver usando um provedor de conteúdo para compartilhar dados apenas entre seus próprios apps, recomendamos 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, proporcionando uma experiência melhor para ele e um acesso mais controlado aos dados do provedor de conteúdo quando os apps que fazem o acesso 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 as flags 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 pela 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 sondar 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 ser equivalente a uma permissão de leitura e gravação.

Permissões

Como o Android isola os aplicativos uns dos outros, eles precisam compartilhar recursos e dados de forma explícita. Isso é feito ao declarar as permissões necessárias para recursos não fornecidos pelo sandbox básico, incluindo o acesso a recursos do dispositivo, como a câmera.

Solicitações de permissões

Minimize 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 app, não é preciso solicitá-la. Consulte o guia para avaliar se o app precisa declarar permissões.

Se possível, projete o aplicativo de forma a não precisar de permissões. Por exemplo, em vez de solicitar acesso a informações do dispositivo para criar um identificador exclusivo, crie um UUID para o app. Saiba mais na seção sobre dados do usuário. Outra opção seria, em vez de usar armazenamento externo (que exige permissão), armazenar os 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 fica exposta a outros apps, como um ContentProvider. Em geral, recomendamos, sempre que possível, usar outros controles de acesso além das permissões confirmadas pelo usuário, já que permissões podem ser confusas para ele. 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ão disponíveis 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 aos dados. Mais detalhes sobre a frequência e os possíveis efeitos desse problema são abordados na pesquisa Re-Delegação de permissão: ataques e defesas (link em inglês), publicada na USENIX.

Definições de permissões

Defina o menor conjunto de permissões que atenda 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 existentes.

Se você precisar de uma nova permissão, considere se a 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 ainda for necessário criar uma nova permissão, declare-a no manifesto do app usando o elemento <permission>. Os apps que usam a nova permissão podem referenciá-la adicionando um elemento <uses-permission> aos arquivos de manifesto. Também é possível adicionar permissões dinamicamente usando o método addPermission().

Se você criar uma permissão com o nível de proteção "perigoso", considere as complexidades abaixo:

  • 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 confusa ou arriscada.
  • Aplicativos podem solicitar a permissão quando o criador dela não foi 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 recomendamos o uso do nível de permissão "perigoso".

Redes

Transações de rede são inerentemente arriscadas para segurança, porque envolvem a transmissão de dados potencialmente particulares 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.

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 sensíveis, como o HttpsURLConnection para o tráfego seguro na Web. Use HTTPS em vez de HTTP sempre que esse protocolo tiver suporte no servidor, já que 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 é altamente encorajado para todos os aplicativos que se comunicam por rede.

Alguns apps usam as portas de rede localhost (link em inglês) para processar uma IPC sensível. Não use essa abordagem, já que 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. A vinculação ao endereço IP não específico INADDR_ANY é pior do que o uso de loopback, já que ela permite que seu aplicativo receba solicitações de qualquer endereço IP.

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.

Rede de telefonia

O protocolo do serviço de mensagens curtas (SMS) foi projetado principalmente para comunicação entre usuários e não é adequado para apps que querem transferir dados. Devido às limitações do SMS, recomendamos o uso do Firebase Cloud Messaging (FCM) e de redes IP para enviar mensagens de dados de um servidor da Web para o app em um dispositivo do usuário.

O SMS não é criptografado nem fortemente autenticado na rede ou no dispositivo. Todo receptor de SMS precisa esperar que a mensagem tenha sido enviada ao seu app por um usuário malicioso. Não use 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 interceptação na rede. No próprio dispositivo Android, as mensagens SMS são transmitidas como intents de transmissão, podendo ser lidas ou capturadas por outros aplicativos que tenham a permissão READ_SMS.

Validação de entrada

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 entrada, e recomendamos que você use esses recursos sempre que possível. Além disso, recomendamos o uso de linguagens com segurança de tipos para reduzir a probabilidade de problemas de validação de entrada.

Se você estiver usando o código nativo, todos os dados lidos de arquivos, recebidos pela rede ou por IPC podem introduzir um problema de segurança. Os problemas mais comuns são overflows de buffer, uso após a liberação e erros off-by-one (links em inglês). O Android oferece várias tecnologias, como ASLR e prevenção de execução de dados (DEP, na sigla em inglês), que reduzem a chance de exploit desses erros, mas não resolvem o problema. 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 (link em inglês).

Se você está usando dados em consultas enviadas a um banco de dados SQL ou um provedor de conteúdo, a injeção de SQL pode ser um problema. A melhor defesa é usar consultas parametrizadas, conforme discutido na seção 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 não for possível usar os recursos de segurança discutidos nesta seção, use formatos de dados bem estruturados e verifique se os dados seguem o formato esperado. Bloquear ou substituir caracteres específicos pode ser uma estratégia eficaz, mas, na prática, essas técnicas são propensas a erros. Recomendamos evitá-las sempre que possível.

Dados do usuário

A melhor abordagem para a segurança de dados do usuário é minimizar o uso de APIs que acessam informações sensíveis ou pessoais. Se você tiver acesso a dados do usuário, evite armazená-los ou transmiti-los sempre que possível. Considere se a lógica do aplicativo pode ser implementada usando um hash ou um formato não reversível dos dados. Por exemplo, ele 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 exploit do seu app por parte de invasores.

Autentique o usuário sempre que o acesso a dados particulares for necessário e use métodos modernos de autenticação, como chaves de acesso e a API Credential Manager. Caso seu app precise acessar informações pessoais, lembre-se de que algumas jurisdições podem exigir que você forneça uma Política de Privacidade que explique como você usa e armazena esses dados. Siga a prática recomendada de segurança de minimizar o acesso aos dados do usuário para simplificar a conformidade.

Considere também se o aplicativo pode expor acidentalmente informações pessoais para outras partes, como componentes de terceiros para publicidade ou serviços de terceiros usados pelo app. 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 diminui a probabilidade de problemas nessa área.

Se o acesso a dados sensíveis for necessário, avalie se essas informações precisam ser transmitidas a um servidor ou se a operação pode ser executada no cliente. Execute todos os códigos que usam dados sensíveis 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 por uma IPC excessivamente permissiva, arquivos universalmente graváveis ou soquetes de rede. A IPC excessivamente permissiva é um caso especial de vazamento de dados protegidos por permissão discutido na seção Solicitações de permissões.

Se um identificador universalmente exclusivo (GUID, na sigla em inglês) for necessário, crie e armazene um número grande e exclusivo. Não use identificadores do smartphone, como o número do telefone ou o IMEI, que podem ser associados a informações pessoais. Esse tópico é discutido com mais detalhes na página sobre práticas recomendadas para identificadores exclusivos.

Tenha cuidado ao gravar 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 smartphone 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, limite o uso de registros em apps de produção. Para implementar o registro com facilidade, use flags de depuração e classes de Log personalizadas com níveis de registro facilmente configuráveis.

WebView

Como a WebView consome conteúdo da Web que pode incluir HTML e JavaScript, o uso inadequado dela pode introduzir problemas comuns de segurança da Web, como scripting em vários sites (injeção de JavaScript, link em inglês). O Android inclui diversos mecanismos para reduzir o escopo desses possíveis problemas limitando a capacidade da WebView à funcionalidade mínima necessária para seu app.

Caso seu aplicativo não use JavaScript diretamente na WebView, não chame setJavaScriptEnabled. Alguns exemplos de código usam esse método. Se você usar como base o exemplo de código que o utiliza em um aplicativo de produção, remova essa chamada do método se ela não for necessária. Por padrão, a WebView não executa JavaScript, impossibilitando o scripting em vários sites.

Use addJavaScriptInterface() com cuidado, já que esse método permite que o JavaScript invoque operações normalmente reservadas para apps Android. Se você usá-lo, exponha o método addJavaScriptInterface() apenas a páginas da Web em que todas as entradas sejam confiáveis. Se a entrada não confiável for permitida, um código JavaScript não confiável poderá invocar métodos do Android no seu app. Em geral, recomendamos expor addJavaScriptInterface() apenas a códigos JavaScript do APK do app.

Caso seu app acesse dados sensíveis com uma WebView, use o método clearCache() para excluir arquivos armazenados localmente. Você também pode usar cabeçalhos do lado do servidor, como no-store, para indicar que um aplicativo não pode armazenar conteúdos específicos em cache.

Dispositivos com plataformas mais antigas que o Android 4.4 (nível 19 da API) usam uma versão do webkit que tem vários problemas de segurança. Como solução alternativa, caso seu app esteja sendo executado nesses dispositivos, ele vai precisar confirmar que os objetos WebView mostrem apenas conteúdo confiável. Para garantir que o app não seja exposto a possíveis vulnerabilidades de SSL, use o objeto de segurança atualizável Provider, conforme descrito em Atualizar seu provedor de segurança para se proteger contra exploits 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.

Solicitações de credencial

As solicitações de credencial são um vetor de ataque. Confira algumas dicas para tornar as solicitações de credenciais nos seus apps Android mais seguras.

Minimize a exposição de credenciais

  • Evite solicitações de credenciais desnecessárias. 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. Solicite apenas a quantidade mínima de informações de credenciais necessárias para autenticação e autorização.
  • Armazene credenciais com segurança. Use a API Credential Manager para ativar a autenticação sem senha com chaves de acesso ou implementar o login federado usando esquemas como o recurso "Fazer login com o Google". Se você precisa usar a autenticação por senha tradicional, não armazene IDs e senhas de usuário no dispositivo. Em vez disso, faça a autenticação inicial com 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.
  • Limite o escopo das permissões. Não solicite permissões amplas para uma tarefa que exija apenas um escopo mais restrito.
  • Limite tokens de acesso. Use operações de tokens de curta duração e chamadas de API.
  • Limite as taxas de autenticação. Solicitações de autenticação ou autorização rápidas e sucessivas podem ser sinal de um ataque de força bruta. Limite essas taxas a uma frequência razoável e, ao mesmo tempo, permita uma experiência funcional e fácil de usar no app.

Use a autenticação segura

  • Implemente chaves de acesso. Ative as chaves de acesso como uma opção mais segura e fácil de usar do que senhas.
  • Adicione biometria. Ofereça a possibilidade de usar autenticação biométrica, como impressão digital ou reconhecimento facial, para aumentar a segurança.
  • Use provedores de identidade federados. A API Credential Manager oferece suporte a provedores de autenticação federados, como o recurso Fazer login com o Google.
  • Criptografe a comunicação. Use HTTPS e tecnologias semelhantes para garantir que os dados enviados pelo seu app por uma rede estejam protegidos.

Pratique o gerenciamento de contas seguro

  • Faça a conexão com serviços acessíveis a vários aplicativos 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 o AccountManager para acessar uma Account, use CREATOR antes de enviar as credenciais para não transmiti-las ao app errado por acidente.
  • Se as credenciais são usadas apenas por aplicativos criados por você, é possível verificar o aplicativo que acessa o AccountManager usando checkSignatures. Como alternativa, se apenas um app usar a credencial, use um KeyStore para armazenamento.

Tome cuidado

  • Mantenha seu código atualizado. Atualize seu código-fonte, incluindo bibliotecas e dependências de terceiros, para se proteger contra as vulnerabilidades mais recentes.
  • Monitore atividades suspeitas. Procure possíveis usos indevidos, como padrões de uso indevido de autorização.
  • Faça uma auditoria do código. Execute verificações de segurança com frequência na sua base de código para procurar possíveis problemas de solicitação de credenciais.

Gerenciamento de chaves de API

As chaves de API são um componente essencial de muitos apps Android, permitindo que eles acessem serviços externos e realizem funções essenciais, como conexão a serviços de mapeamento, autenticação e serviços meteorológicos. No entanto, a exposição dessas chaves confidenciais pode ter consequências graves, incluindo violações de dados, acesso não autorizado e perdas financeiras. Para evitar esses cenários, os desenvolvedores precisam implementar estratégias seguras para lidar com as chaves de API durante todo o processo de desenvolvimento.

Para proteger os serviços contra uso indevido, as chaves de API precisam ser cuidadosamente protegidas. Para proteger uma conexão entre o app e um serviço que usa uma chave de API, é necessário proteger o acesso à API. Quando o app é compilado e o código-fonte dele inclui chaves de API, um invasor pode descompilar o app e encontrar esses recursos.

Esta seção é destinada a dois grupos de desenvolvedores Android: aqueles que trabalham com equipes de infraestrutura no pipeline de entrega contínua e aqueles que implantam apps independentes na Play Store. Nesta seção, descrevemos as práticas recomendadas sobre como processar chaves de API para que o app se comunique com serviços de forma segura.

Geração e armazenamento

Os desenvolvedores precisam tratar o armazenamento de chaves de API como um componente essencial da proteção de dados e da privacidade do usuário usando uma abordagem de defesa em profundidade.

Armazenamento de chaves fortes

Para melhorar a segurança no gerenciamento de chaves, use o Android Keystore e criptografe as chaves armazenadas com uma ferramenta robusta, como a biblioteca security-crypto do Jetpack ou a Tink Java (link em inglês).

O exemplo abaixo usa a biblioteca security-crypto do Jetpack para criar preferências compartilhadas criptografadas.

Kotlin

val masterKey = MasterKey.Builder(context)
  .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
  .build()

val encryptedSharedPreferences = EncryptedSharedPreferences.create(
  context,
  "secret_shared_prefs",
  masterKey,
  EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
  EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

// use the shared preferences and editor as you normally would
encryptedSharedPreferences.edit()

Java

MasterKey masterKey = new MasterKey.Builder(context)
    .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
    .build();

SharedPreferences sharedPreferences = EncryptedSharedPreferences.create(
    context,
    "secret_shared_prefs",
    masterKey,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
);

// use the shared preferences and editor as you normally would
SharedPreferences.Editor editor = sharedPreferences.edit();

Exclusão do controle de origem

Nunca confirme chaves de API no repositório do código-fonte. A inclusão de chaves de API ao código-fonte expõe chaves em repositórios públicos, exemplos de código compartilhados e arquivos compartilhados por acidente. Em vez disso, use plug-ins do Gradle, por exemplo, o secrets-gradle-plugin (link em inglês), para trabalhar com chaves de API no seu projeto.

Chaves específicas do ambiente

Se possível, use chaves de API separadas para ambientes de desenvolvimento, teste e produção. Use chaves específicas para isolar cada ambiente, reduzindo o risco de expor dados de produção e também permitindo desativar chaves comprometidas sem afetar o ambiente de produção.

Controle de uso e acesso

Práticas seguras de chaves de API são essenciais para proteger sua API e seus usuários. Saiba como preparar suas chaves para ter o máximo de segurança:

  • Gere chaves exclusivas para cada app: use chaves de API separadas para cada app a fim de identificar e isolar acessos comprometidos.
  • Implemente restrições de IP: se possível, limite o uso da chave de API a endereços ou intervalos de IP específicos.
  • Limite o uso da chave de apps para dispositivos móveis: faça isso para dispositivos móveis específicos, agrupando-os com a chave ou usando certificados dos apps.
  • Registre e monitore atividades suspeitas: implemente mecanismos de monitoramento e geração de registros de uso da API para detectar atividades suspeitas e evitar possíveis abusos.

Observação: seu serviço precisa fornecer recursos para restringir chaves a uma plataforma ou um pacote específico. Por exemplo, a API Google Maps limita o acesso a chaves por nome de pacote e chave de assinatura.

O OAuth 2.0 fornece um framework para autorizar o acesso a recursos. Ele define padrões de como clientes e servidores devem interagir e permite uma autorização segura. O OAuth 2.0 pode restringir o uso de chaves de API a clientes específicos e definir o escopo de acesso para que cada chave de API tenha apenas o nível mínimo de acesso necessário para a finalidade pretendida.

Rotação e expiração de chaves

Para reduzir o risco de acesso não autorizado devido a vulnerabilidades não identificadas da API, é importante alternar as chaves de API regularmente. O padrão ISO 27001 (link em inglês) define um framework de conformidade para a frequência das rotações de chaves. Na maioria dos casos, um período de rotação de chaves entre 90 dias e 6 meses é adequado. A implementação de um sistema robusto de gerenciamento de chaves ajuda a simplificar esses processos, melhorando a eficiência das necessidades de rotação e expiração das chaves.

Práticas recomendadas gerais

  • Use SSL/HTTPS: sempre use a comunicação HTTPS para criptografar suas solicitações de API.
  • Fixação de certificados: para ter uma camada extra de segurança, implemente a fixação de certificados para conferir quais deles são considerados válidos.
  • Valide e limpe a entrada do usuário: valide e limpe a entrada do usuário para evitar ataques de injeção que podem expor as chaves de API.
  • Siga as práticas recomendadas de segurança: implemente as práticas recomendadas gerais de segurança no processo de desenvolvimento, incluindo técnicas de programação seguras, revisões de código e verificação de vulnerabilidades.
  • Fique por dentro: conheça as práticas recomendadas e ameaças à segurança mais recentes para o gerenciamento de chaves de API.
  • SDKs atualizados: confira se os SDKs e as bibliotecas estão atualizados para a versão mais recente.

Criptografia

Além de oferecer isolamento de dados e suporte à criptografia para todo o sistema de arquivos e canais de comunicação seguros, o Android fornece diversos algoritmos para proteger dados usando a criptografia.

Saiba 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 do framework atual que oferece suporte ao seu caso de uso. Se aplicável, use os provedores fornecidos pelo Google no pedido especificado por ele.

Se você precisar extrair um arquivo de um local de rede conhecido de forma segura, um simples URI HTTPS pode ser adequado sem exigir conhecimento de criptografia. Se precisar de um túnel seguro, use HttpsURLConnection ou SSLSocket, em vez de criar seu próprio protocolo. Se você usa 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á criados, 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 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 funções abaixo:
    • 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úmeros aleatórios 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 extração de chaves criptográficas.

Comunicação entre processos

Alguns apps tentam implementar IPC usando técnicas tradicionais do Linux, como soquetes de rede e arquivos compartilhados. No entanto, recomendamos usar uma funcionalidade do sistema Android para IPC, como Intent, Binder ou um Messenger com um Service e um BroadcastReceiver. Os mecanismos de IPC do Android permitem conferir a identidade do aplicativo que está se conectando à sua IPC e definir políticas de segurança para cada mecanismo.

Muitos dos elementos de segurança são compartilhados entre mecanismos de IPC. Caso seu mecanismo de IPC não seja destinado ao uso por outros aplicativos, 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 quer reprogramar o código.

Caso sua IPC esteja acessível a outros apps, você pode aplicar uma política de segurança usando o elemento <permission>. Se a IPC acontecer entre apps seus que foram assinados com a mesma chave, use uma permissão signature-level no android:protectionLevel.

Intents

Para atividades e broadcast receivers, as intents são o mecanismo mais recomendado para IPCs assíncronas 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 vinculação a um **Service**, use uma intent explícita para manter 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 vai responder à intent, e o usuário não pode conferir qual serviço será iniciado. No Android 5.0 (nível 21 da API) e versões mais recentes, o sistema gera uma exceção se você chama **bindService()** com uma intent implícita.

Transmissões ordenadas podem ser consumidas por um destinatário. Por isso, elas talvez não sejam entregues a todos os aplicativos. Se você estiver enviando uma intent que precisa ser entregue a um destinatário específico, use uma intent explícita que declare o destinatário pelo nome.

Os remetentes de uma intent podem conferir se o destinatário tem uma permissão, especificando uma permissão não nula com a chamada do método. Apenas aplicativos com essa permissão vão receber a intent. Se for possível que os dados em uma intent de transmissão sejam sensíveis, aplique uma permissão para garantir que aplicativos maliciosos 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: filtros de intent não são recursos 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 corretamente 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.

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 nenhum outro aplicativo. No entanto, se você adicionar filtros de intent na declaração do serviço, ele será exportado por padrão. É recomendável declarar explicitamente o atributo android:exported para garantir que ele se comporte da maneira esperada. 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 destinado ao Android 5.0 (nível 21 da API) ou mais recente, use o **JobScheduler**para executar serviços em segundo plano.

Um serviço pode proteger chamadas de IPC individuais feitas nele com permissões. Para fazer isso, chame checkCallingPermission() antes de executar a implementação da chamada. Recomendamos usar as permissões declarativas no manifesto, já que elas estão menos propensas a descuidos.

Atenção: não confunda permissões de cliente e servidor. Confira se o app chamado tem as permissões adequadas e conceda as mesmas permissões ao app de chamada.

Interfaces binder e messenger

O uso de Binder ou Messenger é o mecanismo preferencial para a IPC de estilo RPC no Android. Elas são interfaces bem definidas que possibilitam a autenticação mútua dos endpoints, se necessário.

Recomendamos projetar as interfaces do app de forma a não exigir verificações de permissões específicas da interface. Os objetos Binder e Messenger não são declarados no manifesto do aplicativo. 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 em que são implementados. Se você está criando uma interface que exige autenticação e/ou controles de acesso, precisa 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ê está invocando uma interface fornecida por um Service, a invocação de bindService() pode falhar se você não tiver permissão para acessar o serviço especificado. Se você precisa permitir que um processo externo interaja com o app, mas ele não tem as permissões necessárias, use o método clearCallingIdentity(). Ele executa a chamada para a interface do app como se ele próprio estivesse fazendo a chamada em vez do autor da chamada externo. Você pode restaurar as permissões do autor da chamada mais tarde com o método restoreCallingIdentity().

Para mais informações sobre como executar a IPC com um serviço, consulte Serviços vinculados.

Broadcast receivers

Um BroadcastReceiver gerencia as solicitações assíncronas iniciadas por uma Intent.

Por padrão, os destinatários são exportados e podem ser invocados por qualquer outro aplicativo. Se o BroadcastReceiver for destinado ao uso por outros aplicativos, convém aplicar permissões de segurança a receptores que usam 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.

Segurança com código carregado 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 aumenta a complexidade do gerenciamento de versões e teste de aplicativos, podendo impossibilitar a verificação do comportamento de um app. Por isso, ela pode ser proibida em alguns ambientes.

Se o aplicativo carregar código dinamicamente, o mais importante a se lembrar é que esse código é executado com as mesmas permissões de segurança que o APK do app. O usuário decidiu 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.

Muitos apps tentam carregar códigos de locais inseguros, por exemplo, códigos transferidos por download da rede por protocolos não criptografados ou de locais universalmente graváveis, como um armazenamento externo. Esses locais podem permitir que alguém na rede modifique o conteúdo em trânsito ou que outro aplicativo no dispositivo do usuário modifique o conteúdo no dispositivo. Por outro lado, os módulos incluídos diretamente no APK não podem ser modificados por outros apps. Isso é válido mesmo que o código seja uma biblioteca nativa ou uma classe carregada usando DexClassLoader.

Segurança em uma máquina virtual

O Dalvik é a VM (máquina virtual) de ambiente de execução do Android. O Dalvik foi criado especificamente 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. Outros processos no sistema não podem acessar seu código ou seus dados privados.

Para saber mais sobre segurança de máquinas virtuais, familiarize-se com o material atual 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 com experiência em programação de VMs em outros ambientes, existem duas questões amplas que podem ser diferentes na programação de apps Android:

  • Algumas máquinas virtuais, como a JVM ou o ambiente de execução .NET, funcionam como limites de segurança, isolando o código dos recursos 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 oferece interoperabilidade 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 queiram criar aplicativos modulares e usar o carregamento dinâmico de classes. Ao fazer isso, considere a fonte em que você extrai 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 são verificadas, como fontes de rede não seguras ou armazenamento externo, já que esse código pode ser modificado para incluir comportamentos maliciosos.

Segurança no código nativo

Em geral, é recomendado usar o Android SDK para o desenvolvimento de aplicativos em vez de código nativo com o Android NDK. Apps criados com código nativo são mais complexos, menos portáteis e mais propensos a erros comuns de corrupção de memória, como overflows de buffer.

O Android é criado com o kernel do Linux. Conhecer 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. Uma boa maneira de considerar esse cenário para desenvolvedores com experiência em Linux é saber que cada aplicativo recebe um identificador de usuário exclusivo (UID, na sigla em inglês) com permissões muito limitadas. Isso é discutido em mais detalhes na Visão geral de segurança do Android. Recomendamos se familiarizar com as permissões do aplicativo, mesmo que esteja usando código nativo.