安卓应用通信安全(三)

本文主要介绍 SSL Pinning 相关的知识。文中用到的 vuls 漏洞应用及代码可以在 https://github.com/AndroidAppSec/vuls/releases/tag/v2.2 中下载。

三、SSL Pinning

在上一篇中,我们介绍到,如果在手机中安装 burp 证书的话,就可以使用 burp 来抓取应用的 https 通信,并进行安全测试。推而广之,如果在我们使用恶意 wifi 的过程中,被欺骗安装证书的话,就会遭受 https 的中间人攻击。那么有没有什么办法能够缓解这个风险呢?这就是本文要介绍的技术 SSL Pinning,中文翻译叫做证书固定。

1、SSL Pinning 基本介绍

通过前两篇的介绍,我们知道验证一个证书是否合法主要通过两个方面:证书签发CA 和 hostname 校验。这里有几个问题:一是,证书签发 CA 的根证书私钥有可能泄漏,从而导致有人可以伪造 CA;二是,CA 在签发证书的时候可能会出现失误,导致为其他机构颁发了本机构的证书;三是, hostname 是可以进行伪造的(还记得自签名证书吧)。其实真正标识一个机构身份的是其持有的证书公私钥。所以,目前大部分的SSL Pinning 实现,就是去校验证书的公钥信息,并且很多库默认提供了校验公钥信息的 API。本文我们也主要介绍校验公钥信息的实现。

这里其实有一个问题,公钥信息是公开的,任何人都可以获取到,之前你说 hostname 是可以伪造的,那么自签名证书也可以伪造你这个组织的公钥啊。没错,但是这里的公私钥对是通过非对称加密算法产生的(目前一般为RSA),伪造者是无法通过公钥信息反推出私钥信息的。没有私钥信息,就无法解密 SSL 握手过程的加密信息,从而无法实施中间人攻击。

2、如何获取公钥信息

还是以百度官网为例,其证书信息如下:

分为三级,从上到下依次为 CA 根证书、中间证书和叶证书(真正标识百度的证书)。我们做 SSL Pinning 的时候,可以对这三个证书都进行固定,也可以只选取其中的部分进行固定。推荐的做法是固定叶证书和中间证书,这样既验证了网站所有者信息也验证了证书签发机构信息。

验证公钥信息一般采用对比公钥 hash 之后值是否相同的方式。可以使用以下的脚本获取指定网站证书公钥进行 SHA256 哈希之后再 base64 编码的值。

#!/bin/bash

certs=`openssl s_client -connect $1:443 -showcerts /dev/null | sed -n '/Certificate chain/,/Server certificate/p'`

rest=$certs

while [[ "$rest" =~ '-----BEGIN CERTIFICATE-----' ]]

do

cert="$-----END CERTIFICATE-----"

rest=$

echo `echo "$cert" | grep 's:' | sed 's/.*s:\(.*\)/\1/'`

echo "$cert" | openssl x509 -pubkey -noout |

openssl rsa -pubin -outform der 2>/dev/null |

openssl dgst -sha256 -binary | openssl enc -base64

done

获取百度公钥信息如下(橙色标注为结果):

C = CN, ST = beijing, L = beijing, OU = service operation department, O = "Beijing Baidu Netcom Science Technology Co., Ltd", CN = baidu.com

M9Hz16jmJjXzPUkH8FUKG2GZLd/55sT6yCNiQgwfNtk=

C = BE, O = GlobalSign nv-sa, CN = GlobalSign Organization Validation CA - SHA256 - G2

IQBnNBEiFuhj+8x6X8XLgh01V9Ic5/V3IRQLNFFc7v4=

3、编码实现

主要介绍一下目前安卓下很火的网络框架 OKhttp 的 SSL Pinning 实现。

OKHttp提供了一个类可以方便的设置 SSL Pinning,代码如下:

在 add 方法中增加 pinning 信息:第一个参数为请求的域名,支持 * 通配符;第二个参数为公钥哈希信息,以 / 为分隔符,前半部分指定哈希算法,后半部分为公钥哈希之后进行 base64 编码的值。可以通过 add 方法添加多个 pinning 信息,这些信息之间是 or 的关系。

可以创建多个CertificatePinner,上图为叶证书的 pinner,下图我们加入中间证书的 pinner:

然后,就可以通过以下代码来构造并发起请求了:

多个CertificatePinner之间是 and 的关系。这样我们就同时校验了叶证书和中间证书。

对于OKHttp 来说,还可以通过以下的小技巧来获取 pinning 信息,即在代码中写入一个错误的哈希信息,这样运行的时候,OKHttp 会在错误日志中打印出目前请求网站证书的公钥的相关信息,如下图:

4、后记

以上的代码是将 pinning 信息硬编码在代码中,实现代码简单,但是破解难度就相对较低了。安全性较高的做法是,将证书预埋在客户端,然后再从证书中读取相关信息;或者是将 pinning 信息放入原生层,并进行混淆加密。

SSL Pinning 虽然能够提升 ssl 通信的安全性,但是还是无法避免被绕过的命运,我们会在后续的文章中介绍效果的相关方法和技术。

参考:

https://www.owasp.org/index.php/Certificate_and_Public_Key_Pinning

https://medium.com/@appmattus/android-security-ssl-pinning-1db8acb6621e

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180909G10SK300?refer=cp_1026
  • 腾讯「云+社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。

扫码关注云+社区

领取腾讯云代金券