在非常有限的情况下允许Android应用程序传输明文HTTP流量

5

我已经阅读了 Android 8: Cleartext HTTP traffic not permitted,但是其中的答案都没有满足我想要的。

我有一个Android应用程序,另一个客户端可以通过证书来识别自己。该应用程序希望验证该证书。验证证书的一部分是从证书颁发者获取证书吊销列表(CRL)。 CRL的分发点列在证书中,并且不可避免地是HTTP URL(CRL本身由颁发者签名,因此不存在安全问题,如果它是HTTPS URL,则需要验证保护CRL分发点的证书,并检查其是否已被吊销 ...)

可能的解决方案以及它们为什么对我无效:

  • 不必担心 - 让TLS库负责验证证书。不幸的是,两个客户端之间没有直接的TLS连接;这完全是通过服务器进行介入的(与TLS相连)。
  • 创建 network_security_config.xml 列出允许使用HTTP的域。可悲的是,我在构建应用程序时并不知道URL - 这取决于CA决定放入其证书的内容。
  • 在清单中放置 android:usesCleartextTraffic =“true”。这意味着 任何 流量都可以是HTTP,如果可能的话,我想避免这种情况。 (作为示例,与服务器的通信必须是HTTPS,并且如果我意外使用HTTP,我希望有一个错误。)

代码是否有任何方法可以说“这个 连接允许使用HTTP”(但默认仅限于HTTPS)?


1
注意:我已经在当前答案中添加了注释,说明它们为什么对我无效,但我不想将这些限制编辑到问题中,因为a)它们可能是其他人的好答案;b)编辑问题以使答案无效是不礼貌的。 - Martin Bonner supports Monica
你能事先访问这些证书吗?如果可以,那么您可能能够将它们设置为信任锚点。如果您希望您的 URL 仅支持 HTTPS,则将其作为子域添加到 domain-config 中,并设置 cleartextTrafficPermitted="false" - karan
3个回答

1

NetworkSecurityPolicy.isCleartextTrafficPermitted() 的文档说明如下:

此标志尽最大努力进行处理,因为由于应用程序所提供的访问级别,不可能阻止所有明文流量从 Android 应用程序中传输。例如,没有期望 Socket API 将遵守此标志,因为它无法确定其流量是否为明文。

因此,也许这对您是一个选项:使用 Socket 获取 CRL。有一篇 Daniel Nugent 的帖子描述了如何 设置简单的 TCP 客户端


1
如果您正在使用 OkHttp,您可以按照以下方式构建客户端:
OkHttpClient client = new OkHttpClient.Builder() 
.connectionSpecs(Arrays.asList(ConnectionSpec.MODERN_TLS, ConnectionSpec.COMPATIBLE_TLS))
.build();

这将仅允许通过HTTPS进行连接。因此,您可以使用第三个选项(android:usesCleartextTraffic="true"),当您通过此客户端进行明文连接时,它将失败。
最后,您可以创建一个标准的OkHttp客户端:
OkHttpClient client = new OkHttpClient.Builder().build()

当您想要使用明文连接时。
编辑:使用HttpUrlConnection,您可以简单地检查返回的连接是否是HttpsUrlConnection,例如:
try {
    URL my_url = new URL(path);
    HttpUrlConnection urlConnection = (HttpURLConnection) my_url.openConnection();
    if(!(urlConnection instanceof HttpsURLConnection)) {
        // cleartext connection, throw error
        throw new NotHttpsException();
    }
    // the connection is secure, do normal stuff here
    urlConnection.setRequestMethod("POST");
    urlConnection.setConnectTimeout(1500);
    urlConnection.setReadTimeout(1500);
    result = IOUtil.readFully(urlConnection.getInputStream());
} catch(Exception e) {
    e.printStackTrace()
} finally {
    if(urlConnection != null) urlConnection.disconnect();
}

遗憾的是,我没有使用OkHttp。 - Martin Bonner supports Monica
@MartinBonner,如果您正在使用HttpUrlConnection,我已经添加了一个示例。 - qualverse

0
该应用程序与服务器之间具有TLS连接。
您可以向服务器请求将这些CRL URL交给您(首先它正在交付证书对吧?)。
甚至可以事先执行此操作,并提供包括CRL在内的证书。
通过这种方式,您可以在不丢失https锁定或不必制作异常情况的情况下获取CRL。

嗯,如果可能的话,我宁愿避免信任服务器。这是一个安全通信应用程序。 - Martin Bonner supports Monica
1
如果您可以通过 HTTP 获取 CRL 并且它由 CA 签名(我认为我从问题中理解了这一点),那么为什么信任服务器会更不安全? - Juan
在这个上下文中,“服务器”是专门运行以为该应用程序提供服务的服务器。它与CA完全不同。 - Martin Bonner supports Monica
我并没有这个意思。我实际上理解了你所澄清的内容。在这些条件下,服务器可以获取CRL并将其交给应用程序。我的意思是,假设您使用应用程序进行的连接是通过http(不安全)进行的,则如果您的服务器获取列表(从证书颁发机构),并将它们交给您的应用程序,则没有额外的风险。对于您的应用程序来说,它将是在您的服务器上调用的另一个服务。从连接的角度来看,您的服务器将是位于应用程序和提供CRL的CA颁发机构之间的另一台机器。 - Juan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接