谈谈HTTPS
最近正好在看极客时间的「趣谈网络协议」,目前已经看了大半部分,对于之前 HTTPS 概念比较模糊的地方现在都差不多大致了解了,于是正好选择来记录下。
HTTPS
HTTPS 是在原来的 HTTP 协议基础上进行加密通信,所以本质上 HTTPS 还是基于 TCP/IP 协议进行数据传输,而一个标准的 TCP/IP 协议族中,如下图
HTTPS 的 TLS/SSL 层就工作在应用层和运输层之间。
HTTP 的弊端
在网络传输的过程中,HTTP 是以明文的方式进行交互的,所以在一些重要的交互接口,很容易被中间人截获到报文信息,从而威胁系统安全。
明文
指的是应用层按照 HTTP 协议的标准将信息在网络上传输,例如一个登录接口:有如下两个字段:
1 | { |
只要这个网络包所经过设备,均可以对其进行抓包,从而知道用户的用户名和密码。
如果将时间拨回到2010年附近,那个时候上个网页,经常会弹出一些广告之类的,如果是非服务端返回的,那么就是中间人在这中间篡改了返回的报文。
优化
既然 HTTP 的弊端是报文是明文,那么只要对其加密即可,使得中间人得到的都是密文,这样就算被截获,没有密钥也无法进行解密。
目前常见的加密算法有:「非对称」和「对称」,其中非对称加密的运算速度稍慢于对称加密,因此 HTTPS 选择的是用混合加密算法,即「非对称」算法交换「对称」算法的密钥,后续在数据传输的过程中采用「对称」算法进行数据的处理。
HTTPS 的解决方案
上图是一个完整的 HTTPS 握手图, 其中生成相关密钥的步骤是 3-7,也就说说在第 7 步完成以后,后续的通讯都会基于「对称」加密算法将报文进行加密通信。
那么客户端和服务端是如何约定相关密钥的呢?
客户端和服务端各自会随机生成一个随机数,分别记为 a1、a2,服务端将自己的 CA 证书再下发到客户端,证书里面包含公钥信息,
此时客户端再生成一个随机数字,记为 pre-master,然后通过公钥将 pre-master 进行加密传输给服务端。
这个时候,客户端和服务端均有三个数:a1、a2、pre-master,然后客户端和服务端采用相同的算法,算出一个 secret-master 数,该数就是以后采用的「对称」算法的密钥了。
于是后续便可以通过该密钥进行加密通信了。
中间人攻击
HTTPS 在生成密钥的时候,全部都是明文信息,那么肯顿过存在中间人攻击的情况,具体如下:
如果在客户端和服务端之间有一个中间人,此人生成了自己的证书,并且截获了a1、a2,然后将自己m1、m2 分别给了服务端和客户端,此时 M 所拥有的是 a1、a2、m1、m2、pre-master、服务端CA证书的公钥,自己的CA证书的公私钥。
那么此时 M 就完全可以解密出 C 和 S 之间的通信内容。
其实在业务开发中,如果有过抓包经验,都用过 charles、finder 这类软件,这类软件在抓取 HTTPS 的报文信息的时候,采取的就是上述的中间人方案。
CA 认证
为了避免上述情况发生,需要一个第三方的独立结构为这些证书所背书,因为中间人是不知道真实的服务端的私钥的,因此只要客户端将 pre-matser 通过服务端的公钥进行加密以后,M 就无法得到 pre-master,也就无法算出真正的密钥了。
这个第三方独立结构就是:CA 认证,具体的含义可以看 CA认证
简单来说就是,CA 机构会根据你的域名、持有人信息等等,会采用「非对称」加密算法为你生成一对公私钥,其中公钥内置在证书中,私钥交由申请者保管,并且不允许泄漏。
客户端收到服务端证书以后,会查看其 ROOT 证书是不是被信任的,如果是非授信的,则此次连接会被拒绝,下图失败的的 CA 证书
可以看到百度的顶级证书是 GlobalSign Root CA,而此证书正好已经内置于操作系统中,所以可以确保此次的握手连接中,CA 证书没有被篡改,下图是 mac 系统内置的证书。
CA 认证是如何防止中间人攻击的
还是回到刚才的这个例子。
M 如果给客户端下发的是自己的 CA 证书,那么客户端会验证这份证书的真实性,因为 M 是无法伪造 S 的证书,所以 C 查看证书信息后就会直接拒绝连接,因此可以防止中间人攻击。
如果 M 将 S 的证书原封不动的发给 C,那么由于 M 拿不到该证书对应的私钥,所以也无法进行解密(无法解密也就拿不到pre-master),从而也无法得到最后的「对称」算法的密钥,因此也防止了中间人攻击。
HTTS 连接密钥生成的过程
首先 C 端会额外在生成一个随机数字,称为 pre-master,此时 C 端 会将 pre-master 通过公钥进行加密,然后将密文发送给服务端,此时服务端会通过私钥将密文解成明文,然后得到 pre-master。
此时 C 和 S 端则都通过 PRF 算法,将(a1、a2、pre-master)用算法算出最终的 secret-master,后续的报文就会通过 secret-master 进行加密传输了。
Charles
上述说了 CA 证书是如何防止中间人攻击的,那么 charles 为什么还可以抓得到 HTTPS 的报文呢?
如果我们安装 charles 以后,开启抓包,打开浏览器就会有如下提示:
可以看到浏览器已经检测到了非信任证书了,而我们能抓到 HTTPS 的报文,是因为已经将 charles 的证书信任过了。
此时另一个问题就来了,既然只要将证书信任下就可以抓到 HTTPS 的报文,那么还有不有更加高级的做法呢?
其实查看 HTTPS 的交互流程,之所以出现这个情况,是因为客户端直接信任了中间人的证书,而信任的逻辑是由第三方来决定的,所以才导致了这种情况。
如果不信任任何非官方颁发的 CA 证书,一般是不会有什么问题的。
防止抓包
SSL Pinning
为了解决上述的问题,可以将服务端的证书直接下发到客户端,在握手阶段的时候,就和收到的服务端证书进行对比,如果发现不一致就直接拒绝握手。
锁定方式一般由两种:
- 证书锁定:但是会过期,如果客户端不更新证书,则会导致后续的访问都会出现问题
- 特征锁定:将证书中的某些字节码/公钥提取出来内置于APP中
自定义协议
目前市面上的抓包程序都是针对 HTTP 协议,那么只需要在 TCP/UDP 上自定义应用层的协议,就可以完美的避免抓包了,这里可以看一些美团的一个技术文章:美团点评移动网络优化实践
APP中设置代理检测
这一种就是先检测是否开了代理,如果开了则设置 Proxy 为 NO_PROXY