Ao deixar seu app mais seguro, você ajuda a preservar a confiança do usuário e a integridade do dispositivo.
Está página apresenta diversas práticas recomendadas que têm um impacto positivo e significativo na segurança do app.
Aplicar comunicação segura
Quando você protege os dados trocados entre seu app e outros apps ou entre seu app e um site, você melhora a estabilidade do app e protege os dados enviados e recebidos.
Proteger a comunicação entre apps
Para se comunicar entre apps de forma mais segura, use intents implícitas com um seletor de apps, permissões baseadas em assinatura e provedores de conteúdo não exportados.
Mostrar um seletor de app
Se uma intent implícita puder iniciar pelo menos dois apps no dispositivo de um usuário, mostre um seletor de app de maneira explícita. Essa estratégia de interação permite que os usuários transfiram informações sensíveis para um app de confiança.
Kotlin
val intent = Intent(Intent.ACTION_SEND) val possibleActivitiesList: List<ResolveInfo> = packageManager.queryIntentActivities(intent, PackageManager.MATCH_ALL) // Verify that an activity in at least two apps on the user's device // can handle the intent. Otherwise, start the intent only if an app // on the user's device can handle the intent. if (possibleActivitiesList.size > 1) { // Create intent to show chooser. // Title is something similar to "Share this photo with." val chooser = resources.getString(R.string.chooser_title).let { title -> Intent.createChooser(intent, title) } startActivity(chooser) } else if (intent.resolveActivity(packageManager) != null) { startActivity(intent) }
Java
Intent intent = new Intent(Intent.ACTION_SEND); List<ResolveInfo> possibleActivitiesList = getPackageManager() .queryIntentActivities(intent, PackageManager.MATCH_ALL); // Verify that an activity in at least two apps on the user's device // can handle the intent. Otherwise, start the intent only if an app // on the user's device can handle the intent. if (possibleActivitiesList.size() > 1) { // Create intent to show chooser. // Title is something similar to "Share this photo with." String title = getResources().getString(R.string.chooser_title); Intent chooser = Intent.createChooser(intent, title); startActivity(chooser); } else if (intent.resolveActivity(getPackageManager()) != null) { startActivity(intent); }
Informações relacionadas:
Aplicar permissões baseadas em assinatura
Ao compartilhar dados entre dois apps que você controla ou possui, use permissões baseadas em assinatura. Essas permissões não exigem confirmação do usuário. Em vez disso, elas verificam se os apps que acessam os dados são assinados com a mesma chave de assinatura. Portanto, essas permissões oferecem uma experiência do usuário mais simples e segura.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <permission android:name="my_custom_permission_name" android:protectionLevel="signature" />
Informações relacionadas:
Cancelar permissão de acesso aos provedores de conteúdo do seu app
A menos que você pretenda enviar dados do app para outro que
não seja seu, impeça de maneira explícita que os apps de outros desenvolvedores acessem
os objetos ContentProvider
do seu app. Essa
configuração é importante especialmente caso seu app possa ser instalado em dispositivos
com o Android 4.1.1 (nível 16 da API) ou anterior, uma vez que o atributo android:exported
do elemento
<provider>
é true
por padrão nessas versões do Android.
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp"> <application ... > <provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.myapp.fileprovider" ... android:exported="false"> <!-- Place child elements of <provider> here. --> </provider> ... </application> </manifest>
Pedir credenciais antes de mostrar informações confidenciais
Ao solicitar credenciais dos usuários para que eles possam acessar informações confidenciais ou conteúdo premium no app, peça um PIN/senha/padrão ou credencial biométrica, por exemplo, reconhecimento facial ou impressão digital.
Para saber mais sobre como solicitar credenciais biométricas, consulte o guia sobre autenticação biométrica.
Aplicar medidas de segurança de rede
As seções a seguir descrevem como melhorar a segurança de rede do seu app.
Usar o tráfego TLS
Caso seu app se comunique com um servidor da Web que tenha um certificado emitido por uma autoridade de certificação (CA, na sigla em inglês) conhecida e confiável, use uma solicitação HTTPS como esta:
Kotlin
val url = URL("https://www.google.com") val urlConnection = url.openConnection() as HttpsURLConnection urlConnection.connect() urlConnection.inputStream.use { ... }
Java
URL url = new URL("https://www.google.com"); HttpsURLConnection urlConnection = (HttpsURLConnection) url.openConnection(); urlConnection.connect(); InputStream in = urlConnection.getInputStream();
Adicionar uma configuração de segurança de rede
Se seu app usa CAs novas ou personalizadas, você pode declarar as configurações de segurança da rede em um arquivo de configuração. Esse processo permite que você crie a configuração sem modificar nenhum código do app.
Para adicionar um arquivo de configuração de segurança de rede ao app, siga estas etapas:
- Declare a configuração no manifesto do app:
-
Adicione um arquivo de recurso XML, localizado em
res/xml/network_security_config.xml
.Especifique que todo o tráfego para domínios particulares precisa usar HTTPS desativando a função de limpar texto:
<network-security-config> <domain-config cleartextTrafficPermitted="false"> <domain includeSubdomains="true">secure.example.com</domain> ... </domain-config> </network-security-config>
Durante o processo de desenvolvimento, é possível usar o elemento
<debug-overrides>
para permitir de maneira explícita certificados instalados pelo usuário. Esse elemento modifica as opções básicas de segurança do app durante a depuração e os testes, sem afetar a configuração da versão do app. O snippet a seguir mostra como definir esse elemento no arquivo XML da configuração de segurança de rede do app.<network-security-config> <debug-overrides> <trust-anchors> <certificates src="user" /> </trust-anchors> </debug-overrides> </network-security-config>
<manifest ... > <application android:networkSecurityConfig="@xml/network_security_config" ... > <!-- Place child elements of <application> element here. --> </application> </manifest>
Informações relacionadas: Configurações de segurança de rede
Criar o próprio gerenciador de confiança
Seu verificador de TLS não pode aceitar qualquer certificado. Se uma das seguintes condições for válida para seu caso de uso, pode ser necessário configurar um gerenciador de confiança e processar todos os alertas de TLS que ocorrerem:
- Você se comunica com um servidor da Web que tem um certificado assinado por uma CA nova ou personalizada.
- O dispositivo usado não confia na CA.
- Não é possível usar uma configuração de segurança de rede.
Para saber mais sobre como seguir essas etapas, consulte a discussão sobre como processar uma autoridade de certificação desconhecida.
Informações relacionadas:
Usar objetos WebView com cuidado
Objetos
WebView
no seu app não podem autorizar os usuários a acessar sites que estão fora do
seu controle. Sempre que possível, use uma lista de permissões para restringir o conteúdo carregado
pelos objetos WebView
do app.
Além disso, não ative o
suporte à
interface JavaScript, a menos que você controle e confie totalmente no conteúdo dos
objetos WebView
do seu app.
Usar canais de mensagem HTML
Caso seu app precise usar compatibilidade com a interface JavaScript em dispositivos com o Android 6.0 (API de nível 23) ou mais recente, use canais de mensagem HTML em vez de se comunicar entre um site e o app, conforme mostrado no snippet de código a seguir:
Kotlin
val myWebView: WebView = findViewById(R.id.webview) // channel[0] and channel[1] represent the two ports. // They are already entangled with each other and have been started. val channel: Array<out WebMessagePort> = myWebView.createWebMessageChannel() // Create handler for channel[0] to receive messages. channel[0].setWebMessageCallback(object : WebMessagePort.WebMessageCallback() { override fun onMessage(port: WebMessagePort, message: WebMessage) { Log.d(TAG, "On port $port, received this message: $message") } }) // Send a message from channel[1] to channel[0]. channel[1].postMessage(WebMessage("My secure message"))
Java
WebView myWebView = (WebView) findViewById(R.id.webview); // channel[0] and channel[1] represent the two ports. // They are already entangled with each other and have been started. WebMessagePort[] channel = myWebView.createWebMessageChannel(); // Create handler for channel[0] to receive messages. channel[0].setWebMessageCallback(new WebMessagePort.WebMessageCallback() { @Override public void onMessage(WebMessagePort port, WebMessage message) { Log.d(TAG, "On port " + port + ", received this message: " + message); } }); // Send a message from channel[1] to channel[0]. channel[1].postMessage(new WebMessage("My secure message"));
Informações relacionadas:
Oferecer as permissões corretas
Solicite apenas o número mínimo de permissões necessárias para que o app funcione corretamente. Quando possível, renuncie às permissões quando o app não precisar mais delas.
Usar intents para adiar permissões
Sempre que possível, não adicione uma permissão ao app para concluir uma ação que pode ser concluída em outro app. Em vez disso, use uma intent para adiar a solicitação para outro app que já tenha a permissão necessária.
O exemplo a seguir mostra como usar uma intent para direcionar os usuários a um
app de contatos, em vez de solicitar as
permissões READ_CONTACTS
e WRITE_CONTACTS
:
Kotlin
// Delegates the responsibility of creating the contact to a contacts app, // which has already been granted the appropriate WRITE_CONTACTS permission. Intent(Intent.ACTION_INSERT).apply { type = ContactsContract.Contacts.CONTENT_TYPE }.also { intent -> // Make sure that the user has a contacts app installed on their device. intent.resolveActivity(packageManager)?.run { startActivity(intent) } }
Java
// Delegates the responsibility of creating the contact to a contacts app, // which has already been granted the appropriate WRITE_CONTACTS permission. Intent insertContactIntent = new Intent(Intent.ACTION_INSERT); insertContactIntent.setType(ContactsContract.Contacts.CONTENT_TYPE); // Make sure that the user has a contacts app installed on their device. if (insertContactIntent.resolveActivity(getPackageManager()) != null) { startActivity(insertContactIntent); }
Além disso, se o app precisar realizar E/S com base em arquivos, por exemplo, acessar o armazenamento ou escolher um arquivo, não serão necessárias permissões especiais, porque o sistema poderá concluir as operações por ele. Melhor ainda, depois que o usuário seleciona o conteúdo em um URI específico, o app de chamada recebe a permissão para o recurso selecionado.
Informações relacionadas:
Compartilhar dados de forma segura entre apps
Siga estas práticas recomendadas para compartilhar conteúdos do seu app com outros apps de forma mais segura:
- Aplique permissões somente leitura ou somente gravação, conforme necessário.
-
Conceda aos clientes um acesso único a dados usando as sinalizações
FLAG_GRANT_READ_URI_PERMISSION
eFLAG_GRANT_WRITE_URI_PERMISSION
. - Ao compartilhar dados, use URIs
content://
, e nãofile://
. As instâncias deFileProvider
fazem isso por você.
O snippet de código a seguir mostra como usar sinalizações de concessão de permissões do URI e permissões de provedor de conteúdo para mostrar o arquivo PDF do app em um app visualizador de PDF separado.
Kotlin
// Create an Intent to launch a PDF viewer for a file owned by this app. Intent(Intent.ACTION_VIEW).apply { data = Uri.parse("content://com.example/personal-info.pdf") // This flag gives the started app read access to the file. addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) }.also { intent -> // Make sure that the user has a PDF viewer app installed on their device. intent.resolveActivity(packageManager)?.run { startActivity(intent) } }
Java
// Create an Intent to launch a PDF viewer for a file owned by this app. Intent viewPdfIntent = new Intent(Intent.ACTION_VIEW); viewPdfIntent.setData(Uri.parse("content://com.example/personal-info.pdf")); // This flag gives the started app read access to the file. viewPdfIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); // Make sure that the user has a PDF viewer app installed on their device. if (viewPdfIntent.resolveActivity(getPackageManager()) != null) { startActivity(viewPdfIntent); }
Observação: executar arquivos do diretório inicial do app
gravável é uma
violação W^X.
Por isso, apps não confiáveis direcionados ao Android 10 (API de nível 29) e versões mais recentes não podem
invocar exec()
em arquivos no diretório inicial do app, apenas o
código binário incorporado ao arquivo APK do app.
Além disso, apps direcionados ao Android 10 e versões mais recentes não podem, na
memória, modificar o código executável a partir de arquivos que foram abertos com
dlopen()
. Isso inclui todos os arquivos de objeto compartilhado (.so
)
com realocações de texto.
Informações relacionadas:
android:grantUriPermissions
Armazenar dados de forma segura
Embora seu app possa ter que acessar informações confidenciais dos usuários, eles só vão conceder acesso a esses dados se confiarem que você vai protegê-los de forma adequada.
Armazenar dados privados no armazenamento interno
Armazene todos os dados particulares do usuário no armazenamento interno do dispositivo, que é colocado no sandbox por app. Seu app não precisa solicitar uma permissão para ver esses arquivos e outros apps não podem acessá-los. Como uma medida extra de segurança, quando o usuário desinstala um app, o dispositivo exclui todos os arquivos salvos pelo app no armazenamento interno.
O snippet de código abaixo demonstra uma forma de gravar dados no armazenamento interno:
Kotlin
// Creates a file with this name, or replaces an existing file // that has the same name. Note that the file name cannot contain // path separators. val FILE_NAME = "sensitive_info.txt" val fileContents = "This is some top-secret information!" File(filesDir, FILE_NAME).bufferedWriter().use { writer -> writer.write(fileContents) }
Java
// Creates a file with this name, or replaces an existing file // that has the same name. Note that the file name cannot contain // path separators. final String FILE_NAME = "sensitive_info.txt"; String fileContents = "This is some top-secret information!"; try (BufferedWriter writer = new BufferedWriter(new FileWriter(new File(getFilesDir(), FILE_NAME)))) { writer.write(fileContents); } catch (IOException e) { // Handle exception. }
O snippet de código abaixo mostra a operação inversa, lendo dados do armazenamento interno:
Kotlin
val FILE_NAME = "sensitive_info.txt" val contents = File(filesDir, FILE_NAME).bufferedReader().useLines { lines -> lines.fold("") { working, line -> "$working\n$line" } }
Java
final String FILE_NAME = "sensitive_info.txt"; StringBuffer stringBuffer = new StringBuffer(); try (BufferedReader reader = new BufferedReader(new FileReader(new File(getFilesDir(), FILE_NAME)))) { String line = reader.readLine(); while (line != null) { stringBuffer.append(line).append('\n'); line = reader.readLine(); } } catch (IOException e) { // Handle exception. }
Informações relacionadas:
- Visão geral do armazenamento de dados e arquivos
FileInputStream
FileOutputStream
Context.MODE_PRIVATE
Guardar dados no armazenamento externo com base no caso de uso
Use o armazenamento externo para arquivos grandes e não confidenciais específicos do seu app, assim como arquivos compartilhados com outros apps. As APIs específicas que você usa dependem do app ter sido projetado para acessar arquivos específicos do app ou acessar arquivos compartilhados.
Se um arquivo não contiver informações particulares ou confidenciais, mas tiver valor para o usuário apenas no app, armazene o arquivo em um diretório específico do app no armazenamento externo.
Caso seu app precise acessar ou armazenar um arquivo que tenha valor para outros apps, use uma das seguintes APIs, dependendo do seu caso de uso:
- Arquivos de mídia: para armazenar e acessar imagens, arquivos de áudio e vídeos compartilhados entre apps, use a API Media Store.
- Outros arquivos: para armazenar e acessar outros tipos de arquivos compartilhados, incluindo arquivos transferidos por download, use o framework de acesso ao armazenamento.
Verificar a disponibilidade do volume de armazenamento
Caso seu app interaja com um dispositivo de armazenamento externo removível, lembre-se de que o usuário pode remover o dispositivo de armazenamento enquanto o app estiver tentando acessá-lo. Inclua uma lógica para verificar se o dispositivo de armazenamento está disponível.
Verificar a validade dos dados
Caso seu app use dados de armazenamento externo, verifique se o conteúdo dos dados não foi corrompido ou modificado. Inclua uma lógica para processar arquivos que não estejam mais em um formato estável.
O snippet de código a seguir inclui um exemplo de verificador de hash.
Kotlin
val hash = calculateHash(stream) // Store "expectedHash" in a secure location. if (hash == expectedHash) { // Work with the content. } // Calculating the hash code can take quite a bit of time, so it shouldn't // be done on the main thread. suspend fun calculateHash(stream: InputStream): String { return withContext(Dispatchers.IO) { val digest = MessageDigest.getInstance("SHA-512") val digestStream = DigestInputStream(stream, digest) while (digestStream.read() != -1) { // The DigestInputStream does the work; nothing for us to do. } digest.digest().joinToString(":") { "%02x".format(it) } } }
Java
Executor threadPoolExecutor = Executors.newFixedThreadPool(4); private interface HashCallback { void onHashCalculated(@Nullable String hash); } boolean hashRunning = calculateHash(inputStream, threadPoolExecutor, hash -> { if (Objects.equals(hash, expectedHash)) { // Work with the content. } }); if (!hashRunning) { // There was an error setting up the hash function. } private boolean calculateHash(@NonNull InputStream stream, @NonNull Executor executor, @NonNull HashCallback hashCallback) { final MessageDigest digest; try { digest = MessageDigest.getInstance("SHA-512"); } catch (NoSuchAlgorithmException nsa) { return false; } // Calculating the hash code can take quite a bit of time, so it shouldn't // be done on the main thread. executor.execute(() -> { String hash; try (DigestInputStream digestStream = new DigestInputStream(stream, digest)) { while (digestStream.read() != -1) { // The DigestInputStream does the work; nothing for us to do. } StringBuilder builder = new StringBuilder(); for (byte aByte : digest.digest()) { builder.append(String.format("%02x", aByte)).append(':'); } hash = builder.substring(0, builder.length() - 1); } catch (IOException e) { hash = null; } final String calculatedHash = hash; runOnUiThread(() -> hashCallback.onHashCalculated(calculatedHash)); }); return true; }
Armazenar somente dados não confidenciais em arquivos em cache
Para oferecer um acesso mais rápido a dados não confidenciais do app, armazene-os no cache do
dispositivo. Para caches maiores que 1 MB, use
getExternalCacheDir()
.
Para caches com 1 MB ou menos, use
getCacheDir()
.
Ambos os métodos disponibilizam o objeto
File
, que contém
os dados em cache do app.
O snippet de código a seguir mostra como armazenar em cache um arquivo transferido por download recentemente.
Kotlin
val cacheFile = File(myDownloadedFileUri).let { fileToCache -> File(cacheDir.path, fileToCache.name) }
Java
File cacheDir = getCacheDir(); File fileToCache = new File(myDownloadedFileUri); String fileToCacheName = fileToCache.getName(); File cacheFile = new File(cacheDir.getPath(), fileToCacheName);
Observação: se você usa getExternalCacheDir()
para
colocar o cache do app em armazenamento compartilhado, o usuário pode ejetar a mídia
que contém esse armazenamento enquanto o app está em execução. Inclua uma lógica para
processar adequadamente a ausência de cache que esse comportamento do usuário causa.
Cuidado: nenhum procedimento de segurança é aplicado a esses arquivos.
Portanto, qualquer app direcionado ao Android 10 (API de nível 29) ou a uma versão anterior que tenha a
permissão WRITE_EXTERNAL_STORAGE
pode acessar
o conteúdo desse cache.
Informações relacionadas: Visão geral do armazenamento de dados e arquivos
Usar SharedPreferences no modo privado
Ao usar getSharedPreferences()
para criar ou acessar os objetos SharedPreferences
do app, use MODE_PRIVATE
. Dessa forma, apenas seu app
poderá acessar as informações no arquivo de preferências compartilhadas.
Caso queira compartilhar dados entre apps, não use
objetos
SharedPreferences
. Em vez disso, siga as etapas para compartilhar
dados entre apps de forma segura.
A biblioteca Security também oferece a classe EncryptedSharedPreferences, que une a classe SharedPreferences e criptografa chaves e valores automaticamente.
Informações relacionadas:
Manter os serviços e as dependências atualizados
A maior parte dos apps usa bibliotecas externas e informações do sistema do dispositivo para concluir tarefas específicas. Mantendo as dependências do seu app atualizadas, você torna esses pontos de comunicação mais seguros.
Verificar o provedor de segurança do Google Play Services
Observação: esta seção só é válida para apps destinados a dispositivos em que o Google Play Services esteja instalado.
Caso seu app use o Google Play Services, verifique se ele está atualizado no dispositivo em que o app está instalado. Faça a verificação de forma assíncrona, fora da linha de execução de interface. Se o dispositivo não estiver atualizado, acione um erro de autorização.
Para determinar se o Google Play Services está atualizado no dispositivo, siga as etapas do guia sobre como Atualizar seu provedor de segurança para se proteger contra explorações de SSL.
Informações relacionadas:
Atualizar todas as dependências do app
Antes de implantar seu app, verifique se todas as bibliotecas, SDKs e outras dependências estão em dia:
- Para dependências primárias, como o SDK do Android, use as ferramentas de atualização do Android Studio, como o SDK Manager.
- Para dependências de terceiros, verifique os sites das bibliotecas usadas pelo app e instale todas as atualizações e patches de segurança disponíveis.
Informações relacionadas: Adicionar dependências de build
Mais informações
Para saber mais sobre como aumentar a segurança do seu app, consulte estes recursos:
- Lista de verificação de segurança para a qualidade do app principal
- Programa de melhoria da segurança dos aplicativos
- Canal Android Developers no YouTube (em inglês)
- Codelab Configuração de segurança de rede do Android
- Confirmação protegida pelo Android: como levar a segurança das transações para o próximo nível (em inglês)