关于android:Android-开发中的SSL-pinning

6次阅读

共计 9063 个字符,预计需要花费 23 分钟才能阅读完成。

一、SSL

在日常的平安浸透过程中,咱们常常会遇到瓶颈无处下手,这时候如果攻击者从 APP 进行冲破,往往会有很多惊喜。然而目前市场上的 APP 都会为避免他人歹意盗取和歹意篡改进行一些保护措施,比方模拟器检测、root 检测、APK 加固、代码混同、代码反调试、反脱壳、签名校验等等反抗机制。而测试人员对 APP 进行浸透的首步操作通常就是上 burp 或者 Charles 这类抓包工具进行抓包,查看申请记录里的域名及链接地址是否能够进一步利用。

咱们都晓得 http 协定传输的是明文信息,是能够间接捕捉的,从而造成了数据泄露。为了避免中间人的拦挡,呈现了 HTTPS 加密机制。在 HTTPS 中,应用了证书 + 数字签名解决了抓包的问题,这里用到了两个概念:数字签名和数字证书。

  • 数字签名:是发送方的明文经验了两次加密失去的两个货色组成,一个是 hash,一个是通过私钥加密。
  • 数字证书:其实就是明文 + 数字签名。然而数字证书中的内容远不止这俩,还包含了权威机构的信息,服务器的域名,最重要的是有签名的计算方法,还有就是证书中还包含公钥,公钥用于发放给申请证书的客户端。

SSL(安全套接字层)又被称为 TLS(数据层平安协定),是一种为网络通信提供数据完整性的一种平安协定。它位于 TCP/IP 协定与各种利用协定之间。SSL 协定次要分为两个局部:Handshake Protocol 和 Record Protocol。

Handshake protocol 是用来协商加密通信数据的密钥,Record Protocol 定义传输内容的格局。事实上,HTTPS 就是应用 SSL/TLS 协定进行加密传输,让客户端拿到服务器的公钥,而后客户端随机生成一个对称加密的秘钥,应用公钥加密,传输给服务端,后续的所有信息都通过该对称秘钥进行加密解密,实现整个 HTTPS 的流程。

二、SSL pinning 计划

证书锁定实质是反抗中间人攻打,并非用于反抗抓包破解。但如果程序逻辑未被注入运行在 ” 可信环境 ” 中倒是有些作用。SSL 证书锁定之前,咱们须要了解一些根本的概念:

  • 可信 CA: CA(Certificate Authority)是数字证书认证核心的简称,是指发放、治理、破除数字证书的机构。CA 的作用是查看证书持有者身份的合法性,并签发证书,以防证书被伪造或篡改,以及对证书和密钥进行治理。
  • 双向锁定:在客户端锁定服务端证书的根底上,服务端对客户端的证书也进行锁定,须要客户端再做一次证书预埋。多见于金融业务场景。
  • 证书链:证书链就是 Root CA 签发二级 Intermediate CA,二级 Intermediate CA 能够签发三级 Intermediate CA,也能够间接签发用户证书。从 Root CA 到用户证书之间形成了一个信赖链:信赖 Root CA,就应该信赖它所信赖的二级 Intermediate CA,从而就应该信赖三级 Intermediate CA 直至信赖用户证书。
  • 逐级验证 :客户端对于收到的多级证书,须要从站点证书(leaf certificate) 开始逐级验证,直至呈现操作系统或浏览器内置的受信赖 CA 根证书(root certificate)。

SSL Pinning 技术指的是在应用程序中只信赖固定证书或是公钥。应用程序开发人员在程序中应用 SSL pinning 技术作为利用流量的附加平安层。实现 SSL pinning 的办法次要有两种:证书固定和公钥固定。

  • 证书固定:开发者将 SSL 证书的某些字节码硬编码在用程序中。当应用程序与服务器通信时,它将查看证书中是否存在雷同的字节码。如果存在,则应用程序将申请发送到服务器;如果字节码不匹配,它将抛出 SSL 证书谬误。此技术可避免攻击者应用本人的自签名证书。
  • 公钥固定:在客户拜访网站时进行公钥固定中,服务器将其公钥固定(通过注入)在客户端(客户)浏览器中。当客户端从新拜访同一网站时,服务器将标识其公共密钥以查看连贯的完整性。此技术还能够避免攻击者应用自签名证书。

通常,咱们在测试一个利用的时候会设置代理,从而获取利用在运行过程中的流量。咱们应用代理工具获取流量的话,须要在设施中装置咱们本人的证书到可信赖的根证书中,这样应用程序才会将咱们的证书视为可信赖的无效证书,容许代理工具拦挡利用的流量。然而应用的 SSL pinning 技术的应用程序,只信赖指定的证书,那么咱们就算把咱们的证书装置到设施中,应用程序也不会信赖咱们的证书,这样的话咱们就不能通过代理的形式来拦挡利用的流量。

三、SSL pinning 计划比照

依据安全级别的不同,证书锁定计划大体能够分为如下几种:

安全等级 策略 信赖范畴 破解办法
0 齐全兼容策略 信赖所有证书包含自签发证书 无需非凡操作
1 零碎 / 浏览器默认策略 信赖零碎或浏览器内置 CA 证书、用户装置的证书 设施装置代理证书(用户)
2 System CA pinning 只信赖零碎根证书,不信赖用户装置的证书(android7 反对配置 network-security-config) 注入或者 root 后将用户证书拷贝到零碎证书目录
3 CA pinningRoot(intermediate)certificate pinning 信赖指定 CA 方法的证书 Hook 注入等形式篡改锁定逻辑
4 Leaf Certificate pinning 信赖指定站点证书 Hook 注入等形式篡改锁定逻辑,如遇双向锁定需将 app 自带证书导入代理软件

3.1 站点证书锁定

锁定站点证书是一种比拟平安的做法,然而它有个缺点就是须要保护预埋证书。如果你没思考过更新预埋证书的话就会呈现 SSL 握手失败的提醒。当初的站点证书个别有效期都是在 1 到 2 年,所以做站点证书锁定还要保障服务可用性的话就得必须实现客户端锁定证书指纹的更新。更新站点证书的网络申请通常有如下策略:

  • 指纹更新申请被劫持到的概率比拟低,不锁定更新指纹申请间接应用 https 实现,毛病是安全性稍弱。
  • 自签名证书的有效期十分长,用自签名证书锁定指纹更新申请,毛病是兼容性稍弱。

通常,锁定站点证书时,服务端须要实现证书指纹下发接口。还有每到证书行将过期的时候须要人为的将证书指纹配置到客户端中。提取指纹配置能够由代码实现,然而签发证书是由第三方 CA 实现的,所以此种形式并不是很智能。并且,” 人 ” 为因素的引入会给业务稳定性带来极大危险。

3.2 两头证书锁定

锁定两头证书或根证书的劣势是安全性靠近锁定站点证书,且这两证书的有效期个别很长,能够达到 10 年到 30 年。所以,在不思考热更新证书指纹的状况下,能够应用此种计划。

除了证书有效期工夫长的劣势外,锁定间证书或根证书还能够更好的兼顾业简单的业务场景。因为企业子域名很多状况下都是本人业务的站点证书,然而一个企业通常站点证书都是由一个两头证书 (根证书) 衍生下来的。所以,锁定间证书或根证书不必特地对每个业务线做调整,一套策略或者计划根本能够实用企业整个业务线。

比方,咱们有一个两头证书,到期工夫为 2029 年,对于这么长的工夫窗口,咱们齐全能够让指纹随着利用更新实现迭代。

然而,锁定两头证书的计划会遇到一个问题,那就是更换证书 CA(数字证书颁发机构)。这就须要通过备份一些可能会用的到 CA 指纹,两头证书的量级绝对于根证书要高出很多,而且也不好预测未来可能会更换到哪些两头证书。

3.3 根证书锁定

参考操作系统更新预埋 CA 根证书的机制,咱们能够通过自降级实现锁定 CA 的指纹更新。在 Android N 零碎版本中,内置了 150 多个零碎根证书。而理论作为一个利用是不须要信赖这么多 CA 的根证书的。牢靠卖证书的 CA 就那么十来家,业务的平安需要决定了须要哪种类型的证书,这样备份证书的范畴就收窄了,且根证书的数量级绝对小,所以就没两头证书备份难的问题。

目前支流的 SSL 证书次要分为 DV < OV < EV,安全性和价格是递增的。DV 和 OV 型证书最大的差异是:DV 型证书不蕴含企业名称信息;而 OV 型证书蕴含企业名称信息。

综合看来,根锁定策略的安全性施行难度比拟适宜账号业务。接下来就是备份证书的抉择,备份锁定证书的次要的考量因素:

  • 有效期
  • 安全性
  • 兼容性

例如,上面是 Mac 零碎内置的一些根证书。有效期较长 CA 别离是 HARICA、DigiCert 等。其中,对于那些将在将来某个工夫才会失效的根证书,咱们能够将这类根作为备份,因为这些有效期长且在较早的零碎版本中预埋,阐明兼容性也过关,如果对安全性有更改谋求能够预埋些 EV 证书。

解决根证书的锁定问题后,接下来就是解决到期的问题。比方锁的根证书 2031 年到期,2031 年之后应该如何解决。对于这种场景,咱们能够有如下选项:

  • 回绝连贯,平安优先;
  • 容许连贯,可用优先;
  • 提醒危险让用户抉择,折中策略;

3.4 客户端零碎证书锁定

这个锁定计划绝对前三个要激进许多,安全性晋升也绝对无限,不过能够作为一种增强的计划。咱们须要做的仅仅是将通用操作系统中用户装置的第三方证书移除 APP 的证书信赖列表。并且从 Android7.0 版本开始默认反对此个性,操作的办法也很简略,只须要通过 network-security-config 更改配置即可。

在 Android 7.0 及其当前的版本中,咱们能够通过应用“网络安全性配置”来自定义其平安 (HTTPS、TLS) 连贯的行为,无需批改任何代码,上面是官网对于网络安全配置的阐明。

四、网络安全性配置

上面咱们简略介绍一下,网络安全性配置 network-security-config,如果想要理解更多细节,能够点击查看:网络安全配置的阐明。

4.1 零碎证书锁定

通常,Android 的利用包只有在 release 模式下只能信赖零碎证书,移除用户装置的证书的信赖。

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config>
        <trust-anchors>
            <certificates src="system"/>
        </trust-anchors>
    </base-config>
</network-security-config>

debug 模式下能够退出对信赖用户装置的证书。

<?xml version="1.0" encoding="utf-8"?>
<network-security-config> 
  <base-config> 
    <trust-anchors> 
      <certificates src="system"/> 
    </trust-anchors> 
  </base-config>  
  <debug-overrides> 
    <trust-anchors> 
      <certificates src="system"/>  
      <certificates src="user"/> 
    </trust-anchors> 
  </debug-overrides> 
</network-security-config>

4.2 根证书锁定

在应用根证书锁定时,强制锁定了两个根证书 GoDaddy Class 2 Certification Authority Root Certificate 和 DigiCert。debug 模式下退出对信赖用户装置的证书,超过根证书的工夫时解除锁定。

<?xml version="1.0" encoding="utf-8"?>
 
<network-security-config> 
  <!-- Android N 7.0 以上版本 -->  
  <domain-config> 
    <domain includeSubdomains="true">www.mi.com</domain>  
    <pin-set expiration="2034-06-30"> 
      <!--GoDaddy Class 2 Certification Authority Root Certificate-->  
      <pin digest="SHA-256">VjLZe/p3W/PJnd6lL8JVNBCGQBZynFLdZSTIqcO0SJ8=</pin>  
      <!-- 备份 pin-->  
      <pin digest="SHA-256">VjLZe/p3W/PJnd6lL8JVNBCGQBZynFLdZSTIqcO0SJ8=</pin> 
    </pin-set>  
    <!-- TrustKit Android API -->  
    <trustkit-config enforcePinning="true"> 
      <!-- Add a reporting URL for pin validation reports -->  
      <report-uri>http://report.m.com/log_report</report-uri> 
    </trustkit-config> 
  </domain-config>
  <!-- Debug 模式 -->  
  <debug-overrides> 
    <trust-anchors> 
      <!-- For debugging purposes, add a debug CA and override pins -->  
      <!--<certificates overridePins="true" src="@raw/debugca" />-->  
      <certificates overridePins="true" src="user"/> 
    </trust-anchors> 
  </debug-overrides> 
</network-security-config>

五、设施证书

设施 ROM 发版绝对 app 发版要简单许多,所以设施的证书锁定场景复杂性更高。为了不便阐明,咱们先将设施形象成两大类:零碎自带的证书,比方 AndroidTV 操作系统自带的;另一种是零碎没有预制的证书,比方实时操作系统(RTOS)。如果设施是第一类通用操作系统,则比拟好解决。

  • 如果证书是 CA 签发的,只需信赖零碎证书即可,最好同时开启零碎分区爱护。
  • 如果证书是自签发的,除了信赖零碎证书以外还须要额定只信赖此自签发证书。须要留神的是,切勿为了跑通业务自觉信赖所有证书。

如果,一些业务刚开发的时候可能还没买证书,所以初期代码是信赖所有证书,起初买正式证书后遗记修复证书信赖代码。例如没买证书之前 curl 应用了 - k 参数,买完证书后遗记对立除去此参数。

 -k, --insecure Allow connections to SSL sites without certs (H)

如果设施是第二类 RTOS,首先得须要确认其是否反对 SSL。其上运行的业务是否须要 SSL,如果须要且反对,则能够通过自行预制根再参考前文实现锁定。

六、TrustKit

对于 Android N 及更高版本,咱们能够通过增加了证书形式来保障平安,然而对于 Android N 一以下的版本,咱们须要怎么做呢?此处给大家介绍一款开源 TrustKit,它能够在 network_security_config.xml 文件中应用雷同的格局来增加对 Android N 下版本的反对。

上面咱们就来说说 TrustKit 的应用。首先,在我的项目的 app/build.gradle 文件增加如下依赖。

implementation 'com.datatheorem.android.trustkit:trustkit:<last_version>'

而后,关上 network_security_config.xml 文件,增加如下内容:

<!-- res/xml/network_security_config.xml -->
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <!-- Pin the domain www.datatheorem.com -->
  <!-- Official Android N API -->
  <domain-config>
    <domain>www.datatheorem.com</domain>
    <pin-set>
      <pin digest="SHA-256">k3XnEYQCK79AtL9GYnT/nyhsabas03V+bhRQYHQbpXU=</pin>
      <pin digest="SHA-256">2kOi4HdYYsvTR1sTIR7RHwlf2SescTrpza9ZrWy7poQ=</pin>
    </pin-set>
    <!-- TrustKit Android API -->
    <!-- Do not enforce pinning validation -->
    <trustkit-config enforcePinning="false">
      <!-- Add a reporting URL for pin validation reports -->
      <report-uri>http://report.datatheorem.com/log_report</report-uri>
    </trustkit-config>
  </domain-config>
  <debug-overrides>
    <trust-anchors>
      <!-- For debugging purposes, add a debug CA and override pins -->
      <certificates overridePins="true" src="@raw/debugca" />
    </trust-anchors>
  </debug-overrides>
</network-security-config>

实现上述配置之后,在 AndroidManifest.xml 文件中引入下面的配置文件,如下。

<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application 
         android:networkSecurityConfig="@xml/network_security_config"
        ... >
        ...
    </application>
</manifest>

当然,咱们也能够通过代码的形式来引入 TrustKit 并进行相干的配置。

@Override
protected void onCreate(Bundle savedInstanceState) {super.OnCreate(savedInstanceState);


  // Using the default path - res/xml/network_security_config.xml
  TrustKit.initializeWithNetworkSecurityConfiguration(this);


  // OR using a custom resource (TrustKit can't be initialized twice)
  TrustKit.initializeWithNetworkSecurityConfiguration(this, R.xml.my_custom_network_security_config);


  URL url = new URL("https://www.datatheorem.com");
  String serverHostname = url.getHost();
  
  //Optionally add a local broadcast receiver to receive PinningFailureReports
  PinningValidationReportTestBroadcastReceiver receiver = new PinningValidationReportTestBroadcastReceiver();
          LocalBroadcastManager.getInstance(context)
                  .registerReceiver(receiver, new IntentFilter(BackgroundReporter.REPORT_VALIDATION_EVENT));


  // HttpsUrlConnection
  HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
  connection.setSSLSocketFactory(TrustKit.getInstance().getSSLSocketFactory(serverHostname));


  // OkHttp 2.x
  OkHttpClient client =
    new OkHttpClient()
        .setSslSocketFactory(OkHttp2Helper.getSSLSocketFactory());
  client.interceptors().add(OkHttp2Helper.getPinningInterceptor());
  client.setFollowRedirects(false);


  // OkHttp 3.0.x, 3.1.x and 3.2.x
  OkHttpClient client =
    new OkHttpClient.Builder()
        .sslSocketFactory(OkHttp3Helper.getSSLSocketFactory())
        .addInterceptor(OkHttp3Helper.getPinningInterceptor())
        .followRedirects(false)
        .followSslRedirects(false)


  // OkHttp 3.3.x and higher
  OkHttpClient client =
    new OkHttpClient.Builder()
        .sslSocketFactory(OkHttp3Helper.getSSLSocketFactory(), OkHttp3Helper.getTrustManager())
        .addInterceptor(OkHttp3Helper.getPinningInterceptor())
        .followRedirects(false)
        .followSslRedirects(false)
    .build();}


class PinningFailureReportBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {PinningFailureReport report = (PinningFailureReport) intent.getSerializableExtra(BackgroundReporter.EXTRA_REPORT);
    }
}

对于 SSL pinning 的常识就介绍这么多,有疑难欢送留言。

正文完
 0