Skip to content

MASTG-TEST-0021: Teste de Verificação de Identidade de Endpoint

Visão Geral

Análise Estática

Utilizar TLS para transportar informações sensíveis pela rede é essencial para a segurança. No entanto, criptografar a comunicação entre um aplicativo móvel e sua API backend não é trivial. Os desenvolvedores frequentemente optam por soluções mais simples, porém menos seguras (por exemplo, aquelas que aceitam qualquer certificado) para facilitar o processo de desenvolvimento, e às vezes essas soluções fracas chegam à versão de produção, potencialmente expondo os usuários a ataques Man-in-the-Middle (MITM)). Consulte "CWE-295: Improper Certificate Validation".

Duas questões principais devem ser abordadas:

  • Verificar se um certificado vem de uma fonte confiável, ou seja, de uma AC (Autoridade Certificadora) confiável.
  • Determinar se o servidor de destino apresenta o certificado correto.

Certifique-se de que o nome do host e o próprio certificado são verificados corretamente. Exemplos e armadilhas comuns estão disponíveis na documentação oficial do Android. Procure no código exemplos de uso de TrustManager e HostnameVerifier. Nas seções abaixo, você pode encontrar exemplos do tipo de uso inseguro que deve procurar.

Observe que, a partir do Android 8.0 (API level 26), não há suporte para SSLv3 e o HttpsURLConnection não fará mais fallback para um protocolo TLS/SSL inseguro.

Verificando a Versão do SDK de Destino

Aplicativos que têm como destino o Android 7.0 (API level 24) ou superior usarão uma Configuração de Segurança de Rede padrão que não confia em nenhum CA fornecido pelo usuário, reduzindo a possibilidade de ataques MITM que induzem os usuários a instalar CAs maliciosos.

Decodifique o aplicativo usando o apktool ( Explorando o Pacote do Aplicativo) e verifique se o targetSdkVersion no apktool.yml é igual ou superior a 24.

grep targetSdkVersion UnCrackable-Level3/apktool.yml
  targetSdkVersion: '28'

No entanto, mesmo se targetSdkVersion >=24, o desenvolvedor pode desativar as proteções padrão usando uma Configuração de Segurança de Rede personalizada que define uma âncora de confiança personalizada forçando o aplicativo a confiar em CAs fornecidos pelo usuário. Consulte "Analisando Âncoras de Confiança Personalizadas").

Analisando Âncoras de Confiança Personalizadas

Procure o arquivo de Configuração de Segurança de Rede) e inspecione quaisquer <trust-anchors> personalizados que definam <certificates src="user"> (o que deve ser evitado).

Você deve analisar cuidadosamente a precedência das entradas:

  • Se um valor não estiver definido em uma entrada <domain-config> ou em uma <domain-config> pai, as configurações em vigor serão baseadas no <base-config>
  • Se não estiver definido nesta entrada, serão usadas as configurações padrão).

Veja este exemplo de uma Configuração de Segurança de Rede para um aplicativo com destino ao Android 9 (API level 28):

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <domain-config>
        <domain includeSubdomains="false">owasp.org</domain>
        <trust-anchors>
            <certificates src="system" />
            <certificates src="user" />
        </trust-anchors>
    </domain-config>
</network-security-config>

Algumas observações:

  • Não há <base-config>, o que significa que a configuração padrão) para Android 9 (API level 28) ou superior será usada para todas as outras conexões (apenas CA system será confiável em princípio).
  • No entanto, o <domain-config> substitui a configuração padrão, permitindo que o aplicativo confie em CAs system e user para o <domain> indicado (owasp.org).
  • Isso não afeta subdomínios devido a includeSubdomains="false".

Resumindo, podemos traduzir a Configuração de Segurança de Rede acima para: "o aplicativo confia em CAs do sistema e do usuário para o domínio owasp.org, excluindo seus subdomínios. Para quaisquer outros domínios, o aplicativo confiará apenas nos CAs do sistema".

Verificando o Certificado do Servidor

O TrustManager é um meio de verificar as condições necessárias para estabelecer uma conexão confiável no Android. As seguintes condições devem ser verificadas neste ponto:

  • O certificado foi assinado por uma CA confiável?
  • O certificado expirou?
  • O certificado é autoassinado?

O seguinte trecho de código às vezes é usado durante o desenvolvimento e aceitará qualquer certificado, substituindo as funções checkClientTrusted, checkServerTrusted e getAcceptedIssuers. Tais implementações devem ser evitadas e, se forem necessárias, devem ser claramente separadas das builds de produção para evitar falhas de segurança incorporadas.

TrustManager[] trustAllCerts = new TrustManager[] {
    new X509TrustManager() {
        @Override
        public X509Certificate[] getAcceptedIssuers() {
            return new java.security.cert.X509Certificate[] {};
        }

        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType)
            throws CertificateException {
        }
    }
 };

// SSLContext context
context.init(null, trustAllCerts, new SecureRandom());

Verificação de Certificado do Servidor no WebView

Às vezes, os aplicativos usam um WebView para renderizar o site associado ao aplicativo. Isso é comum em frameworks baseados em HTML/JavaScript, como o Apache Cordova, que usa um WebView interno para interação do aplicativo. Quando um WebView é usado, o navegador móvel realiza a validação do certificado do servidor. Ignorar qualquer erro TLS que ocorra quando o WebView tenta se conectar ao site remoto é uma má prática.

O seguinte código ignorará problemas de TLS, exatamente como a implementação personalizada do WebViewClient fornecida ao WebView:

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new WebViewClient(){
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        //Ignora erros de certificado TLS e instrui o WebViewClient a carregar o site
        handler.proceed();
    }
});

Verificação de Certificado no Apache Cordova

A implementação do uso interno do WebView do framework Apache Cordova ignorará erros TLS no método onReceivedSslError se a flag android:debuggable estiver ativada no manifesto do aplicativo. Portanto, certifique-se de que o aplicativo não seja depurável. Consulte o caso de teste "Testando se o Aplicativo é Depurável".

Verificação de Nome do Host

Outra falha de segurança em implementações TLS do lado do cliente é a falta de verificação de nome do host. Ambientes de desenvolvimento geralmente usam endereços internos em vez de nomes de domínio válidos, então os desenvolvedores frequentemente desativam a verificação de nome do host (ou forçam um aplicativo a permitir qualquer nome de host) e simplesmente se esquecem de alterar isso quando o aplicativo vai para produção. O seguinte código desativa a verificação de nome do host:

final static HostnameVerifier NO_VERIFY = new HostnameVerifier() {
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
};

Com um HostnameVerifier incorporado, é possível aceitar qualquer nome de host:

HostnameVerifier NO_VERIFY = org.apache.http.conn.ssl.SSLSocketFactory
                             .ALLOW_ALL_HOSTNAME_VERIFIER;

Certifique-se de que seu aplicativo verifica um nome do host antes de estabelecer uma conexão confiável.

Análise Dinâmica

Ao testar um aplicativo com destino ao Android 7.0 (API level 24) ou superior, ele deve estar aplicando efetivamente a Configuração de Segurança de Rede e você não deve conseguir ver o tráfego HTTPS descriptografado inicialmente. No entanto, se o aplicativo tiver como destino API levels abaixo de 24, o aplicativo aceitará automaticamente os certificados de usuário instalados.

Para testar a verificação inadequada de certificado, inicie um ataque MITM usando um proxy de interceptação como o Burp. Tente as seguintes opções:

  • Certificado autoassinado:
  • No Burp, vá para a aba Proxy e selecione a aba Options.
  • Vá para a seção Proxy Listeners, destaque seu listener e clique em Edit.
  • Vá para a aba Certificate, marque Use a self-signed certificate e clique em Ok.
  • Execute seu aplicativo. Se você conseguir ver o tráfego HTTPS, seu aplicativo está aceitando certificados autoassinados.
  • Aceitando certificados com uma CA não confiável:
  • No Burp, vá para a aba Proxy e selecione a aba Options.
  • Vá para a seção Proxy Listeners, destaque seu listener e clique em Edit.
  • Vá para a aba Certificate, marque Generate a CA-signed certificate with a specific hostname e digite o nome do host do servidor backend.
  • Execute seu aplicativo. Se você conseguir ver o tráfego HTTPS, seu aplicativo está aceitando certificados com uma CA não confiável.
  • Aceitando nomes de host incorretos:
  • No Burp, vá para a aba Proxy e selecione a aba Options.
  • Vá para a seção Proxy Listeners, destaque seu listener e clique em Edit.
  • Vá para a aba Certificate, marque Generate a CA-signed certificate with a specific hostname e digite um nome de host inválido, por exemplo, example.org.
  • Execute seu aplicativo. Se você conseguir ver o tráfego HTTPS, seu aplicativo está aceitando todos os nomes de host.

Se você ainda não conseguir ver nenhum tráfego HTTPS descriptografado, seu aplicativo pode estar implementando certificate pinning).