跳过内容

MASTG-TEST-0021:测试端点身份验证

已弃用测试

此测试已**弃用**,不应再使用。**原因**:MASTG V2 中提供新版本

请查看以下涵盖此 v1 测试的 MASTG v2 测试

概述

静态分析

使用 TLS 在网络上传输敏感信息对于安全性至关重要。然而,加密移动应用程序与其后端 API 之间的通信并非易事。开发人员通常选择更简单但不安全的解决方案(例如,那些接受任何证书的解决方案),以方便开发过程,有时这些薄弱的解决方案进入生产版本,可能将用户暴露于中间人 (MITM)攻击。请参阅“CWE-295:不正确的证书验证”

应解决两个关键问题

  • 验证证书是否来自可信来源,即可信 CA(证书颁发机构)。
  • 确定端点服务器是否呈现了正确的证书。

确保主机名和证书本身已正确验证。示例和常见陷阱可在官方 Android 文档中找到。在代码中搜索TrustManagerHostnameVerifier用法的示例。在以下部分中,您可以找到应该注意的不安全用法示例。

请注意,从 Android 8.0(API 级别 26)开始,不再支持 SSLv3,并且HttpsURLConnection将不再回退到不安全的 TLS/SSL 协议。

验证目标 SDK 版本

面向 Android 7.0(API 级别 24)或更高版本的应用程序将使用一个默认网络安全配置,该配置不信任任何用户提供的 CA,从而减少了诱使用户安装恶意 CA 的 MITM 攻击的可能性。

使用 apktool 解码应用程序( 探索应用程序包),并验证 apktool.yml 中的targetSdkVersion是否等于或高于24

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

但是,即使targetSdkVersion >=24,开发人员也可以使用自定义网络安全配置定义一个自定义信任锚来禁用默认保护,强制应用程序信任用户提供的 CA。请参阅“分析自定义信任锚”

分析自定义信任锚

搜索网络安全配置文件,并检查任何定义<certificates src="user">(应避免)的自定义<trust-anchors>

您应该仔细分析条目的优先级

  • 如果未在<domain-config>条目或父<domain-config>中设置值,则使用的配置将基于<base-config>
  • 如果未在此条目中定义,则将使用默认配置

请看以下面向 Android 9(API 级别 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>

一些观察结果

  • 没有<base-config>,这意味着 Android 9(API 级别 28)或更高版本的默认配置将用于所有其他连接(原则上只会信任system CA)。
  • 但是,<domain-config>覆盖了默认配置,允许应用程序信任指定<domain> (owasp.org) 的systemuser CA。
  • 由于includeSubdomains="false",这不会影响子域。

将所有内容放在一起,我们可以将上述网络安全配置翻译为:“该应用程序信任 owasp.org 域(不包括其子域)的系统和用户 CA。对于任何其他域,该应用程序将仅信任系统 CA”。

验证服务器证书

TrustManager是一种验证 Android 中建立信任连接所必需条件的方法。此时应检查以下条件

  • 证书是否由受信任的 CA 签名?
  • 证书是否已过期?
  • 证书是否是自签名的?

以下代码片段有时在开发期间使用,并且将接受任何证书,覆盖函数checkClientTrustedcheckServerTrustedgetAcceptedIssuers。应避免此类实现,如果需要,应将其与生产版本明确分开,以避免内置安全漏洞。

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());

WebView 服务器证书验证

有时,应用程序使用 WebView 呈现与应用程序关联的网站。基于 HTML/JavaScript 的框架(例如 Apache Cordova)就是这种情况,它使用内部 WebView 进行应用程序交互。当使用 WebView 时,移动浏览器将执行服务器证书验证。忽略 WebView 尝试连接到远程网站时发生的任何 TLS 错误是一种不好的做法。

以下代码将忽略 TLS 问题,就像提供给 WebView 的 WebViewClient 自定义实现一样

WebView myWebView = (WebView) findViewById(R.id.webview);
myWebView.setWebViewClient(new WebViewClient(){
    @Override
    public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
        //Ignore TLS certificate errors and instruct the WebViewClient to load the website
        handler.proceed();
    }
});

Apache Cordova 证书验证

如果在应用程序清单中启用了标志android:debuggable,则 Apache Cordova 框架的内部 WebView 用法的实现在方法onReceivedSslError中将忽略TLS 错误。因此,请确保应用程序不是可调试的。请参阅测试用例“测试应用程序是否可调试”。

主机名验证

客户端 TLS 实现中的另一个安全漏洞是缺少主机名验证。开发环境通常使用内部地址而不是有效的域名,因此开发人员通常禁用主机名验证(或强制应用程序允许任何主机名),并且在应用程序投入生产时忘记更改它。以下代码禁用了主机名验证

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

使用内置的HostnameVerifier,接受任何主机名是可能的

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

确保您的应用程序在设置信任连接之前验证主机名。

动态分析

测试面向 Android 7.0(API 级别 24)或更高版本的应用程序时,它应该有效地应用网络安全配置,并且您起初不应看到解密的 HTTPS 流量。但是,如果应用程序面向低于 24 的 API 级别,则应用程序将自动接受已安装的用户证书。

要测试不正确的证书验证,请使用拦截代理(例如 Burp)启动 MITM 攻击。尝试以下选项

  • 自签名证书
  • 在 Burp 中,转到Proxy选项卡,然后选择Options选项卡。
  • 转到Proxy Listeners部分,突出显示您的侦听器,然后单击Edit
  • 转到Certificate选项卡,选中Use a self-signed certificate,然后单击Ok
  • 运行您的应用程序。如果您能够看到 HTTPS 流量,则您的应用程序正在接受自签名证书。
  • 接受具有不受信任的 CA 的证书
  • 在 Burp 中,转到Proxy选项卡,然后选择Options选项卡。
  • 转到Proxy Listeners部分,突出显示您的侦听器,然后单击Edit
  • 转到Certificate选项卡,选中Generate a CA-signed certificate with a specific hostname,然后键入后端服务器的主机名。
  • 运行您的应用程序。如果您能够看到 HTTPS 流量,则您的应用程序正在接受具有不受信任的 CA 的证书。
  • 接受不正确的主机名
  • 在 Burp 中,转到Proxy选项卡,然后选择Options选项卡。
  • 转到Proxy Listeners部分,突出显示您的侦听器,然后单击Edit
  • 转到Certificate选项卡,选中Generate a CA-signed certificate with a specific hostname,然后键入一个无效的主机名,例如 example.org。
  • 运行您的应用程序。如果您能够看到 HTTPS 流量,则您的应用程序正在接受所有主机名。

如果您仍然无法看到任何解密的 HTTPS 流量,则您的应用程序可能正在实现证书固定