As interações criptografadas entre cliente e servidor usam o Transport Layer Security (TLS, link em inglês) para proteger os dados do app.
Este artigo discute as práticas recomendadas relacionadas a protocolos de rede seguros e considerações sobre a infraestrutura de chave pública (ICP). Leia a Visão geral da segurança do Android e a Visão geral de permissões para mais detalhes.
Conceitos
Um servidor com um certificado TLS tem uma chave pública e uma chave privada correspondente. O servidor usa a criptografia de chave pública (link em inglês) para assinar o certificado durante o handshake de TLS.
Um handshake simples prova apenas que o servidor conhece a chave privada do certificado. Para resolver essa situação, deixe o cliente confiar em vários certificados. Um determinado servidor não é confiável se o certificado dele não aparecer no conjunto de certificados confiáveis do lado do cliente.
No entanto, os servidores podem usar a rotação de chaves para mudar a chave pública do certificado para uma nova. Para mudar a configuração do servidor, é necessário atualizar o app cliente. Se o servidor for um serviço da Web de terceiros, como um navegador da Web ou app de e-mails, é mais difícil saber quando atualizar o app cliente.
Os servidores geralmente dependem de certificados de autoridades certificadoras (ACs, link em inglês) para emitir certificados, o que mantém a configuração do lado do cliente mais estável ao longo do tempo. Uma AC assina (link em inglês) um certificado do servidor usando a chave privada dela. O cliente pode verificar se o servidor tem um certificado de AC conhecido pela plataforma.
As ACs confiáveis geralmente são listadas na plataforma host. O Android 8.0 (nível 26 da API) inclui mais de 100 ACs que são atualizadas em cada versão e não mudam de um dispositivo para outro.
Os apps clientes precisam de um mecanismo para verificar o servidor, já que a AC oferece certificados para vários servidores. Ela identifica o servidor por um nome específico, como gmail.com, ou de um caractere curinga, como *.google.com.
Para mais informações sobre o certificado do servidor de um site, use o comando s_client
da ferramenta
openssl
(link em inglês), transmitindo o número da porta. Por padrão, o HTTPS usa a porta 443.
O comando transmite a saída openssl s_client
para o
openssl x509
, que formata as informações do certificado no
padrão X.509 (link em inglês). O comando solicita o tema
(nome do servidor) e o emissor (AC).
openssl s_client -connect WEBSITE-URL:443 | \ openssl x509 -noout -subject -issuer
Um exemplo de HTTPS
Supondo que você tenha um servidor da Web com um certificado emitido por uma AC conhecida, faça uma solicitação segura, conforme mostrado no código abaixo:
Kotlin
val url = URL("https://wikipedia.org") val urlConnection: URLConnection = url.openConnection() val inputStream: InputStream = urlConnection.getInputStream() copyInputStreamToOutputStream(inputStream, System.out)
Java
URL url = new URL("https://wikipedia.org"); URLConnection urlConnection = url.openConnection(); InputStream in = urlConnection.getInputStream(); copyInputStreamToOutputStream(in, System.out);
Para personalizar solicitações HTTP, transmita para HttpURLConnection
.
A documentação HttpURLConnection
do Android inclui
exemplos de processamento de cabeçalhos de solicitação e resposta, publicação de conteúdo, gerenciamento de cookies, uso
de proxies, armazenamento de respostas em cache e muito mais. O framework do Android verifica certificados e nomes de host
usando essas APIs.
Use essas APIs sempre que possível. A seção abaixo aborda problemas comuns que exigem soluções diferentes.
Problemas comuns ao verificar certificados de servidor
Suponha que, em vez de retornar conteúdo, getInputStream()
gere uma exceção:
javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found. at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:374) at libcore.net.http.HttpConnection.setupSecureSocket(HttpConnection.java:209) at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.makeSslConnection(HttpsURLConnectionImpl.java:478) at libcore.net.http.HttpsURLConnectionImpl$HttpsEngine.connect(HttpsURLConnectionImpl.java:433) at libcore.net.http.HttpEngine.sendSocketRequest(HttpEngine.java:290) at libcore.net.http.HttpEngine.sendRequest(HttpEngine.java:240) at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:282) at libcore.net.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:177) at libcore.net.http.HttpsURLConnectionImpl.getInputStream(HttpsURLConnectionImpl.java:271)
Isso pode ocorrer por vários motivos, incluindo:
- A AC que emitiu o certificado do servidor era desconhecida.
- O certificado do servidor foi autoassinado em vez de assinado por uma AC.
- A configuração do servidor não incluía uma AC intermediária.
As seções abaixo discutem como solucionar esses problemas e manter sua conexão com o servidor segura.
Autoridade certificadora desconhecida
A SSLHandshakeException
ocorre porque o sistema não confia na AC. Isso pode acontecer porque você tem um certificado de
uma nova AC em que o Android não confia ou porque seu app está operando em uma versão anterior sem
a AC. Por ser particular, uma AC raramente é conhecida. Normalmente, a AC é desconhecida por não ser
pública, ou seja, é uma AC particular emitida por uma organização como o governo, uma corporação
ou uma instituição de ensino para uso interno.
Para confiar em ACs personalizadas sem precisar mudar o código do app, mude a Configuração de segurança de rede.
Cuidado:
muitos sites descrevem uma solução alternativa insuficiente, que consiste em instalar um
TrustManager
que não faz nada.
Isso deixa os usuários vulneráveis a ataques ao usar um ponto de acesso Wi-Fi público, porque um
invasor pode usar truques de DNS para enviar o tráfego dos usuários por um proxy que finge
ser seu servidor. Com isso, o invasor pode
registrar senhas e outros dados pessoais. Isso funciona porque o invasor pode gerar um
certificado e, sem um TrustManager
que confirme que o certificado vem de uma fonte
confiável, não é possível bloquear esse tipo de ataque. Então, não faça isso, nem temporariamente. Em vez disso,
faça com que o app confie no emissor do certificado do servidor.
Certificado de servidor autoassinado
Uma SSLHandshakeException
pode ocorrer devido a um certificado autoassinado, tornando o servidor a própria AC. Esse processo é semelhante a uma autoridade certificadora
desconhecida. Modifique a configuração de segurança de rede do seu aplicativo para confiar nos
certificados autoassinados.
Autoridade certificadora intermediária ausente
Uma SSLHandshakeException
ocorre devido à ausência da AC intermediária. As ACs públicas raramente assinam certificados do servidor. Em vez disso, a
AC raiz assina ACs intermediárias.
Para reduzir o risco de comprometimento, as ACs mantêm a AC raiz off-line. No entanto, sistemas operacionais como o Android normalmente confiam apenas em ACs raiz diretamente, deixando uma pequena lacuna de confiança entre o certificado do servidor (assinado pela AC intermediária) e o verificador de certificado, que reconhece a AC raiz.
Para remover essa lacuna de confiança, o servidor envia uma cadeia de certificados da AC do servidor usando intermediários para uma AC raiz confiável durante o handshake de TLS.
Por exemplo, esta é a cadeia de certificados do mail.google.com
como percebida pelo comando s_client
openssl
(link em inglês):
$ openssl s_client -connect mail.google.com:443 --- Certificate chain 0 s:/C=US/ST=California/L=Mountain View/O=Google LLC/CN=mail.google.com i:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA 1 s:/C=ZA/O=Thawte Consulting (Pty) Ltd./CN=Thawte SGC CA i:/C=US/O=VeriSign, Inc./OU=Class 3 Public Primary Certification Authority ---
Isso mostra que o servidor envia um certificado para mail.google.com emitido pela AC Thawte SGC, que é uma AC intermediária, e um segundo certificado para a AC Thawte SGC emitido por uma AC Verisign, que é a CA principal em que o Android confia.
No entanto, é possível que um servidor não esteja configurado para incluir a AC intermediária necessária. Por exemplo, este é um servidor que pode causar um erro em navegadores do Android e exceções em apps Android:
$ openssl s_client -connect egov.uscis.gov:443 --- Certificate chain 0 s:/C=US/ST=District Of Columbia/L=Washington/O=U.S. Department of Homeland Security/OU=United States Citizenship and Immigration Services/OU=Terms of use at www.verisign.com/rpa (c)05/CN=egov.uscis.gov i:/C=US/O=VeriSign, Inc./OU=VeriSign Trust Network/OU=Terms of use at https://www.verisign.com/rpa (c)10/CN=VeriSign Class 3 International Server CA - G3 ---
Ao contrário de uma AC desconhecida ou um certificado de servidor autoassinado, a maioria dos navegadores para computador não gera um erro durante a comunicação com esse servidor. Os navegadores de computador armazenam em cache ACs intermediárias confiáveis. Depois de reconhecer uma AC intermediária de um site, o navegador não vai precisar mais dela na cadeia de certificados.
Alguns sites fazem isso intencionalmente para servidores da Web secundários que fornecem recursos. Para economizar largura de banda, eles podem mostrar a página HTML principal de um servidor com uma cadeia completa de certificados, mas as imagens, CSS e JavaScript sem a AC. Às vezes, esses servidores podem fornecer um serviço da Web que você está tentando acessar pelo seu app Android, que não é tão tolerante.
Para corrigir esse problema, configure o servidor para incluir a AC intermediária na cadeia do servidor. A maioria das ACs fornece instruções sobre como fazer isso para servidores da Web comuns.
Avisos sobre o uso direto do SSLSocket
Até o momento, os exemplos se concentraram no HTTPS usando HttpsURLConnection
.
Às vezes, os apps precisam usar TLS separado do HTTPS. Por exemplo, um app de e-mail pode usar variantes de TLS
como SMTP, POP3 ou IMAP. Nesses casos, o app pode usar SSLSocket
diretamente, da mesma forma que HttpsURLConnection
faz internamente.
As técnicas descritas até agora
para solucionar problemas de verificação de certificado também se aplicam ao SSLSocket
.
Na verdade, ao usar um TrustManager
personalizado, o que é transmitido para
HttpsURLConnection
é uma SSLSocketFactory
.
Então, se precisar usar um TrustManager
personalizado com um
SSLSocket
, siga
as mesmas etapas e use essa SSLSocketFactory
para criar seu
SSLSocket
.
Cuidado:
o SSLSocket
não realiza a verificação de nomes de host. Seu app
precisa fazer a própria verificação do nome do host, de preferência chamando getDefaultHostnameVerifier()
com o nome de host esperado.
Além disso, esteja ciente de que HostnameVerifier.verify()
não gera uma exceção em caso de erro. Em vez disso, esse método retorna um resultado booleano que você precisa
verificar explicitamente.
Validação de certificado
O TLS depende de ACs para emitir certificados apenas para os proprietários verificados de servidores e domínios. Em casos raros, ACs são fraudadas ou, no caso de Comodo ou DigiNotar (links em inglês), violadas. Isso faz com que os certificados de um nome de host sejam emitidos por indivíduos que não são proprietários do servidor ou domínio.
Para reduzir esse risco, o Android processa a revogação de certificados em todo o sistema, usando uma combinação de uma lista de bloqueio e a transparência de certificados, sem depender da verificação de certificados on-line. Além disso, o Android vai validar as respostas do OCSP fixadas no handshake TLS.
Para ativar a transparência de certificados no seu app, consulte a seção Ativar a transparência de certificados na nossa documentação de configuração de segurança de rede.
Como restringir seu app a certificados específicos
Cuidado: a fixação de certificados, que é a prática de restringir os certificados considerados válidos para seu app àqueles que você autorizou anteriormente, não é recomendada para apps Android. Mudanças futuras na configuração do servidor, como mudar para outra AC, renderizam apps com certificados fixados que não podem se conectar ao servidor sem receber uma atualização do software do cliente.
Se você quiser restringir o app para aceitar somente certificados especificados, é essencial incluir vários PINs de backup, incluindo pelo menos uma chave totalmente sob seu controle e um período de validade suficientemente curto para evitar problemas de compatibilidade. A Configuração de segurança de rede oferece fixação com esses recursos.
Certificados do cliente
Este artigo se concentra no uso de TLS para proteger comunicações com servidores. O TLS também
oferece suporte ao conceito de certificados do cliente que permitem que o servidor valide a identidade de um
cliente. Embora estejam além do escopo deste artigo, as técnicas necessárias são semelhantes à especificação de
um TrustManager
personalizado.
Nogotofail: uma ferramenta de teste de segurança do tráfego de rede
A Nogotofail é uma ferramenta que proporciona uma forma fácil de confirmar que seus apps estão seguros contra vulnerabilidades e erros de configuração conhecidos do TLS/SSL. Ela é uma ferramenta automatizada, eficaz e escalonável para testar problemas de segurança de rede em qualquer dispositivo cujo tráfego de rede possa passar por ela.
A Nogotofail é útil em três casos de uso principais:
- Encontrar erros e vulnerabilidades.
- Verificar correções e administrar regressões.
- Entender quais aplicativos e dispositivos geram qual tráfego.
A Nogotofail é compatível com Android, iOS, Linux, Windows, ChromeOS, macOS e com qualquer dispositivo que possa ser conectado à Internet. Há um cliente disponível para definir as configurações e receber notificações no Android e no Linux, e o próprio mecanismo de ataque pode ser implantado como roteador, servidor VPN ou proxy.
Você pode acessar essa ferramenta no projeto de código aberto Nogotofail (em inglês).
Atualizações de SSL e TLS
Android 10
Alguns navegadores, como o Google Chrome, permitem que os usuários escolham um certificado quando um servidor TLS envia uma
mensagem de solicitação de certificado como parte de um handshake de TLS. No Android 10 e versões mais recentes, os objetos KeyChain respeitam os
emissores e os parâmetros de especificação da chave ao chamar KeyChain.choosePrivateKeyAlias()
para mostrar aos usuários
uma solicitação de seleção de certificado. Em particular, esse prompt não contém opções que não atendem
às especificações do servidor.
Se não houver certificados selecionáveis pelo usuário disponíveis, como acontece quando nenhum certificado corresponde à especificação do servidor ou um dispositivo não possui nenhum certificado instalado, o prompt de seleção de certificado não vai aparecer.
Além disso, não é necessário ter um bloqueio de tela do dispositivo para importar chaves ou certificados de AC para um objeto KeyChain no Android 10 ou versões mais recentes.
TLS 1.3 ativado por padrão
No Android 10 e versões mais recentes, o TLS 1.3 é ativado por padrão para todas as conexões TLS. Confira alguns detalhes importantes sobre nossa implementação do TLS 1.3:
- Os conjuntos de criptografia do TLS 1.3 não podem ser personalizados. Os conjuntos de criptografia do TLS 1.3 com suporte são sempre
ativados quando o TLS 1.3 está ativado. Todas as tentativas de desativá-los chamando
setEnabledCipherSuites()
serão ignoradas. - Quando o TLS 1.3 é negociado, os objetos
HandshakeCompletedListener
são chamados antes que as sessões sejam adicionadas ao cache da sessão. No TLS 1.2 e em outras versões anteriores, esses objetos são chamados depois que as sessões são adicionadas ao cache da sessão. - Em algumas situações em que as instâncias do SSLEngine geram uma
SSLHandshakeException
em versões anteriores do Android, em vez disso, essas instâncias geram umaSSLProtocolException
no Android 10 e versões mais recentes. - Não há suporte ao modo 0-RTT.
Se quiser, você pode extrair um SSLContext que tenha o TLS 1.3 desativado chamando
SSLContext.getInstance("TLSv1.2")
. Você também pode ativar ou desativar versões de protocolo em cada conexão, chamando setEnabledProtocols()
em um objeto apropriado.
Certificados assinados com SHA-1 não são confiáveis no TLS
No Android 10, os certificados que usam o algoritmo de hash SHA-1 não são confiáveis nas conexões TLS. As ACs raiz não emitem esse certificado desde 2016 e não são mais confiáveis no Chrome nem nos outros principais navegadores.
Todas as tentativas de conexão vão falhar ao tentar se conectar a um site com um certificado usando SHA-1.
Mudanças e melhorias no comportamento do KeyChain
Alguns navegadores, como o Google Chrome, permitem que os usuários escolham um certificado quando um servidor TLS envia uma
mensagem de solicitação de certificado como parte de um handshake de TLS. No Android 10 e versões mais recentes,
os objetos KeyChain
respeitam
os emissores e os parâmetros de especificação da chave ao chamar KeyChain.choosePrivateKeyAlias()
para mostrar
aos usuários uma solicitação de seleção de certificado. Em particular, esse prompt não contém opções que não
atendem às especificações do servidor.
Se não houver certificados selecionáveis pelo usuário disponíveis, como acontece quando nenhum certificado corresponde à especificação do servidor ou um dispositivo não possui nenhum certificado instalado, o prompt de seleção de certificado não vai aparecer.
Além disso, não é necessário ter um bloqueio de tela do dispositivo para importar chaves ou certificados de AC para um objeto KeyChain no Android 10 ou versões mais recentes.
Outras mudanças de TLS e criptografia
Houve várias pequenas mudanças nas bibliotecas de TLS e criptografia que entraram em vigor no Android 10:
- As criptografias AES/GCM/NoPadding e ChaCha20/Poly1305/NoPadding retornam tamanhos de buffer mais precisos
de
getOutputSize()
. - O pacote de criptografia TLS_FALLBACK_SCSV é omitido das tentativas de conexão com um protocolo máximo de TLS 1.2 ou mais recente. Devido a melhorias nas implementações do servidor TLS, não recomendamos a tentativa de substituto externo ao TLS. Em vez disso, recomendamos confiar na negociação da versão do TLS.
- ChaCha20-Poly1305 é um alias para ChaCha20/Poly1305/NoPadding.
- Nomes de host com pontos à direita não são considerados nomes de host SNI válidos.
- A extensão supported_signature_algorithms na CertificateRequest é respeitada ao escolher uma chave de assinatura para respostas de certificado.
- Chaves de assinatura opacas, como as do Android Keystore, podem ser usadas com assinaturas RSA-PSS no TLS.
Mudanças na conexão HTTPS
Se um app executado no Android 10 transmitir um valor nulo para a setSSLSocketFactory()
, ocorre uma IllegalArgumentException
. Nas versões anteriores, transmitir um valor nulo para setSSLSocketFactory()
tinha o mesmo efeito que a transmissão da fábrica padrão atual.
Android 11
Soquetes SSL usam o mecanismo SSL do Conscrypt por padrão
A implementação padrão do SSLSocket do Android é baseada em Conscrypt
(link em inglês). Desde o Android 11, essa implementação é criada internamente com base no SSLEngine do Conscrypt.