导语:前言 随着数据隐私变得越来越重要,谷歌一直在试图增强移动操作系统的功能,用以保护Android移动设备和端点的所有数据。Android 9.0 P(Pie)预计在8月发布,其网络通信将默认为TLS。为了防止APP连接失败,Android移动应用程序开发人员将
前言
随着数据隐私变得越来越重要,谷歌一直在试图增强移动操作系统的功能,用以保护Android移动设备和端点的所有数据。Android 9.0 P(Pie)预计在8月发布,其网络通信将默认为TLS。为了防止APP连接失败,Android移动应用程序开发人员将需要更新其后端服务,以支持HTTPS或实现Android网络安全配置功能。
当Android 6.0 Marshmallow发布时,谷歌提出了把android:usesCleartextTraffic作为防止意外使用明文通信的手段,Android 7.0 Nougat通过引入Android网络安全配置特性来扩展这个属性,这使得开发人员进一步规范了安全通信。Android的网络安全配置被称为Network Security Configuration,它是开发人员为Android应用程序定制的一个XML文件。
你们中的一些人可能认为这听起来很熟悉,因为iOS也使用类似的客户端检查配置,就是我们所说的App Transport Security。虽然与NSAppTranportSecurity相比,Network Security Configuration所提供的保护与其有很多相似之处。但它们在各自的平台上采取了截然不同的方法来实现网络安全。在这里不做赘述,让我们来看看在Android移动应用程序中使用Network Security Configuration的几个好处,并了解如何将其投入到实现该功能的最佳实践中。
实施优点
1.防止回归到明文通信
一条铁则:安全性乃重中之重。Android Network Security Configuration功能提供了一个简单的层,用来保护应用程序在未加密的明文中意外传输的敏感数据。
如果你不知道“未加密的通信”意味着什么,那么让我们设想这样一个情景,假设你的办公室有一个规定——必须通过UPS来运送所有货物。一个新的实习生加入了办公室,负责运送货物到全国各地的办公室。但是实习生忘记了这项规定,并通过一个未知的、不太昂贵的服务来承包了所有货物的转运。Android Network Security Configuration就像是一个发送和接收的管理器,它检查所有入站和出站的货物,并在设备进入未经审核的交付系统之前停止装运,它可以用来防止意外地使用不可信的、未加密的连接。
Android 9最大的变化之一是在默认情况下,cleartextTrafficPermitted允许被设置为false。这意味着,如果你没有看到这个标志被明确设置为false,并且应用程序的API级别低于28,则该标志将被判断为true。
在Network Security Config中使用的cleartextTrafficPermitted标志的另一个能力,是在特定域和子域上执行真正的设置:
Network Security ConfigurationShell
<network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">insecure.example.com</domain> </domain-config> <base-config cleartextTrafficPermitted="false" /> </network-security-config>
2.为安全连接设置可信证书颁发机构
可信证书机构(Trusted Certificate Authorities,CA)可以充当信任中介。在前文所说的故事中,办公室安全策略是将货物托付给UPS,但是也可以把业务交给其他物流公司,比如说FedEx、DHL等。归根结底,你信任谁来安全地发送应用程序数据并防止中间人攻击(MITM)呢?而现在,开发人员可以使用Android Network Security Configuration功能来指定他们信任哪些CA来颁发证书,并确保通信的安全。
首先,Android Network Security Configuration给开发者提供了一些可供选择的CA,默认情况下,Android 7 +(Nougat, Oreo and Pie)所使用的信任锚将是预先安装的系统CA证书,称为“system(系统)”:
Trust Anchors: Android 7+Shell
<network-security-config> <base-config> <trust-anchors> <certificates src="system"/> </trust-anchors> </base-config> </network-security-config>
在Android 6 (Marshmallow)及其以下的,默认的信任锚还将包括用户安装的证书,称为“user(用户)”:
Trust Anchors: Android 6 and belowShell
<network-security-config> <base-config> <trust-anchors> <certificates src="system"/> <certificates src="user"/> </trust-anchors> </base-config> </network-security-config>
最后,可以设置自定义信任锚:
Trust Anchors: CustomShell
<network-security-config> <base-config> <trust-anchors> <certificates src="@raw/my_custom_ca"/> </trust-anchors> </base-config> </network-security-config>
3.实施证书锁定
实施证书锁定又增加了一层安全性。让我们重新回顾一下货物运输的故事,如果可信的CA是诸如UPS、FedEx等的公司,那么证书锁定类似于指定你所信任公司的哪项服务来运送你的货物。Android Network Security Configuration特征可以用来限制仅由可信CA颁发的特定证书用以通信。
我们讨论了在以前所研究过的实现证书锁定的方法,在下面的示例中,我们可以针对特定域和子域,将引脚设置为备份,并设置有效日期。
Certificate Pinning with Network Security ConfigShell
<network-security-config> <domain-config> <domain includeSubdomains="true">example.com</domain> <pin-set expiration="2018-01-01"> <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> <!-- backup pin --> <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin> </pin-set> </domain-config> </network-security-config>
4.调试应用程序网络连接
Android Network Security Configuration中提供的另一个选项是调试重写。此特性允许你在Network Security Config中设置只有当android:debuggable为true时才可用。例如,你可以使用自定义CA,为质量保证/预生产环境配置自定义信任锚,这会简化封闭环境测试,因为应用商城不接受标记为可调试的应用程序。
Debug OverridesShell
<network-security-config> <debug-overrides> <trust-anchors> <certificates src="@raw/debug_cas"/> </trust-anchors> </debug-overrides> </network-security-config>
实施建议
你已经了解了部署Network Security Configuration的一些好处,现在让我们来介绍一些实现此文件的最佳实践。
首先,检查应用程序清单,看看它是否有这个特性。查找android:networkSecurityConfig,类似于这样:
Sample Manifest with Network Security ConfigShell
<manifest ... > <application android:networkSecurityConfig="@xml/network_security_config" ... > ... </application> </manifest>
一旦找到了Network Security Configuration文件,就到了检查如何允许明文通信的时候了。
下面的示例代码是一个反例:
Bad Example: cleartext Permissions with Network Security ConfigShell
<network-security-config> <domain-config cleartextTrafficPermitted="false"> <domain includeSubdomains="true">example.com</domain> <domain includeSubdomains="true">cdn.example2.com</domain> </domain-config> <base-config cleartextTrafficPermitted="true" /> </network-security-config>
虽然这确保了所有到example.com和cdn.example2.com的通信都是通过HTTPS发送的,但是发送给其他域的所有通信都默认可以是明文,这完全违背了Network Security Configuration功能的预期目的——加强保护通过Android设备传输的所有数据的隐私。
如果你的移动应用程序必须以明文发送数据,那么这样做只会加密某些域的通信。此外,还必须仔细检查端点,明确允许HTTP检查敏感数据和其他API相关问题:
Recommended Implementation of cleartext PermissionsShell
<network-security-config> <domain-config cleartextTrafficPermitted="true"> <domain includeSubdomains="true">insecure.example.com</domain> <domain includeSubdomains="true">insecure.cdn.example.com</domain> </domain-config> <base-config cleartextTrafficPermitted="false" /> </network-security-config>
另一个要考虑的是关于cleartextTrafficPermitted标志,它在Android 8及更低版本的默认值为true,因此,在所有应用程序的Network Security Configuration中,要明确将其设置为false。
让我们看看下面的另一个反例:
Bad Example: Default Implementation of Trust Anchors for Android 6 and belowShell
<network-security-config> <base-config> <trust-anchors> <certificates src="system"/> <certificates src="user"/> </trust-anchors> </base-config> </network-security-config>
在此示例中,我们看到应用程序正在接受其信任锚中的用户证书,这意味着应用程序将以与系统预置证书相同的方式,来信任用户安装的证书。配置应用程序使用的信任锚时,最好仅限制对系统证书的信任,并在必要时限制应用程序内构建的自定义CA,这有助于防止攻击者能够在设备上安装证书来进行中间人攻击(MITM)。作为Android 7中引入的保护措施的一部分,默认情况下,用户安装的证书不像预先安装的证书那样受信任。如前所述, Android 6及更低版本默认接受用户证书,因此,在必要时明确选择“system(系统)”和/或自定义CA并在所有应用中排除“user(用户)”非常重要。
Recommended Implementation of Trust Anchors with Custom CA (When Necessary)Shell
<network-security-config> <base-config> <trust-anchors> <certificates src="system"/> <certificates src="@raw/CustomCA"/> </trust-anchors> </base-config> </network-security-config>
让我们再来看一个锁定反例:
Bad Example: Certificate Pinning with Network Security ConfigShell
<network-security-config> <domain-config> <domain includeSubdomains="true">example.com</domain> <pin-set> <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> </pin-set> </domain-config> </network-security-config>
上面的例子揭示了应用程序中的两个潜在问题。首先,没有设置pin-set的有效期,第二,没有备份引脚——一个聪明的策略是要设置证书有效期并有多个备份引脚的,旋转四个引脚并不是闻所未闻。如果没有明确设置pin的有效期,则应用程序在到期后将无法连接,但是如果你设置了一个有效期,当pin协议失效时,应用程序不是无法连接,而是将故障转移到设备上的系统CA。
Recommended Implementation of Certificate Pinning with Network Security ConfigShell
<network-security-config> <domain-config> <domain includeSubdomains="true">example.com</domain> <pin-set expiration="2018-01-01"> <pin digest="SHA-256">7HIpactkIAq2Y49orFOOQKurWxmmSFZhBCoQYcRhJ3Y=</pin> <!-- backup pin --> <pin digest="SHA-256">fwza0LRMXouZHRC8Ei+4PyuldPDcf3UKgO/04cDM1oE=</pin> </pin-set> </domain-config> </network-security-config>
最后,让我们来谈谈Network Security Config调试的重写。
Recommended Implementation of Debug Overrides with Network Security ConfigShell
<network-security-config> <debug-overrides> <trust-anchors> <certificates src="@raw/debug_cas"/> </trust-anchors> </debug-overrides> </network-security-config>
虽然调试功能可以帮助消除应用程序中的调试网络代码,但请务必删除应用程序中的任何调试CA。作为一个最佳操作,应避免在你的应用程序中留下任何不必要的信息,因为这可能会带来安全风险,特别是当内部CA证书包含在应用程序的最终产品构建中时。
小结
从安全分析员的角度来看,知道如何在网络安全配置中读取和发现问题是很重要的。正如我们所看到的,在安装之前,我们可以在应用程序中找到一些潜在的问题,但是请记住,这些发现本身并不能证明你的应用程序的网络连接是安全的。
首先,你需要确定你的应用程序是否正在执行主机名验证,因为Network Security Configuration的保护不能解决此类问题。然后,确保你的第三方库符合Network Security Configuration,如果不这样做的话,可能会导致你的应用程序出现问题。此外,Network Security Configuration不受较低级别的网络连接(如websockets)的支持。最后你要记住,移动中的网络相关问题只是移动测试总体范围的一小部分。
总体而言,Android Network Security Configuration为Android提供了许多简单的网络安全功能,如果你的应用程序目前没有利用Network Security Configuration,你必须在未来一年中使用它。
与Android P的发布同步,谷歌已经开始在应用程序商城中实施目标API级别,以减少移动操作系统碎片,并将用户推向当前版本。虽然谷歌计划逐步增加每一个新版本的数量,但是目前的需求仍是26,不过到明年这个时候,API级别30(Android Q)很可能会被淘汰,这意味着如果你的应用程序使用HTTP,则必须在Network Security Configuration文件中声明,因为那时的强制目标API级别将为28。