98b7ef5cad50d63191b27d8814c52793
Android 架构之网络安全演进

文末可获取进入小专栏读者群方式。

正文

如果你在支付时输入的支付密码被拦截了怎么办?如果你和密友的微信聊天记录被中间人监听怎么办?如果你发布的朋友圈图片/视频被恶意下载怎么办?近几年网络数据安全问题频发,不仅是开发者,连普通用户都越来越意识到了网络数据安全的重要性。

在小专栏前面几篇文章里我们谈了如何搭建高可用的网络架构如何加快网络连接和数据传输如何搭建长连接等技术点,这次我们再来谈一个非常重要的技术点,也是面试很爱考的知识点:如何保障我们的网络数据安全

这几年各个厂家都在推Https,Apple更是强制Https,越来越多的开发者也意识到了数据安全的重要性,并渐渐成为一项必备技能。目前TLS 1.3协议还在草案阶段,阿里、微信都纷纷基于TLS 1.3协议自研出了一套TLS安全协议,采用了最新的安全算法和最优的握手协议,比如微信MMTLS,这些都会在下文中涉及。

本文主要包括下面的知识模块:

  • 自己动手实现网络数据加密方案(Https)
    • (非)对称加密算法
    • 客户端如何获取会话密钥
    • 服务端生成会话密钥,进行非对称加密下发
    • 客户端主动生成密钥
    • 公钥的合法性校验
  • Https的弊端
    • 2-RTT的延时
    • 非对称加密CPU开销
  • Https握手加速
    • Session ID
    • 蚂蚁金服采用的Session Ticket
  • 0-RTT的TLS 1.3草案
  • 微信/阿里等自研TLS 1.3安全协议
    • 密钥协商方式
    • 认证加密
    • 防重放机制

I. 自己动手实现网络数据加密

前面的文章中提到的无论是长连接还是短连接,其最终传输的json或二进制数据都是明文传输的。为了提高安全性,现在最普遍采用的是Https对数据进行加密。

网上有很多Https实现机制的介绍,这里我们换一种思路来讲解,如果要你自己实现数据加密,如何做?

我们的第一步,是实现一套基本可用的加密方案。下面一起来设计这套方案吧:

1. (非)对称加密算法

前面既然说我们的数据是明文传输,那么我就对数据做下加密吧。常用的加密有下面两种:

  • 对称加密比如AES
  • 非对称加密比如RSA

而我们知道RSA计算量比较大,对于CPU有一定的性能开销。而且对于大型客户端而言,平均每个接口的QPS可能能达到数十万,如果这些都是短连接,那就意味着每次数据传输都需要做RSA加密,那还得了。

既然非对称加密开销太大,那我们就用对称加密,这意味着,客户端和服务端需要持有一把相同的密钥,这里我们称为会话密钥,然后在数据传输之前,先用密钥加密,在收到数据后再用密钥解密,而且性能开销也不大。

2. 客户端如何获取会话密钥

嗯,那问题来了,服务端还好说,如何让客户端获取这把密钥呢?我们能想到下面两种方案:

  • 内置在安装包内
  • App启动后从服务端获取

第一种方案是最简单的,改动最小的。但是,如果安装包一旦被人破解了,取出了密钥,那也就意味着所有数据他们可以轻松解密,更重要的是,我们只能通过发新包来更换密钥,显然这样是不可靠的。

那从服务端获取呢,这样可以动态替换密钥,如果发生密钥泄漏,那么就重新下发。但这里存在一个悖论,就是这个密钥的获取也是从网络下发的,而这个请求显然是明文的,因此黑客只需要拦截你这个请求,读出密钥就可以轻松破解后续的请求数据了。

那我们如何安全的去下发又不被拦截呢?

显然,我们必须对这个会话密钥进行加密才能够进行下发。但对称加密必须双方都有相同的密钥才行,这时,我们又想到了非对称加密

3. 服务端生成会话密钥,进行非对称加密下发

对于非对称加密我们知道:其中存在两把密钥:公钥和私钥,公钥加密的内容只能用私钥解密,反之,私钥加密的内容只能用公钥解密。

这样说的话,我们可以把公钥下发给客户端,然后服务端自动生成一个会话密钥,用私钥加密后传输给客户端,客户端收到后再用公钥解密能安全获取这个会话密钥了。

咋一看这个会话密钥是加密后在网络上传输的,但实际上里面存在一个问题:由于公钥是公开的,因此任何人都能用公钥解开并获取你的会话密钥。因此,这个方案也不够完善。

那怎么办呢?

我们再回顾一下这个方案,发现存在一个本质的问题:这个会话密钥是服务端单独生成的,那一定会导致一种结果:只要客户端能最终解密出来,那黑客也一定能解密出来。因为对于服务端而言,客户端和黑客都是密钥的接收方,两者是平等的,无法区别的。

因此,我们考虑另一种方式:让客户端主动生成密钥。

4. 客户端主动生成密钥

具体方式如下:

  1. 服务端明文下发公钥给客户端;
  2. 客户端生成一个随机数作为会话密钥,利用公钥加密后发送给服务端;
  3. 服务端收到后,通过私钥对密文进行解密并获取随机数。

可以看出这个过程里,黑客就算拦截了我们的加密数据,也会因为没有私钥而无法获取会话密钥

当然了,在真实的Https握手过程里,除了这个随机数,之前客户端和服务端会生成另外两个随机数,最终由这3个随机数共同生成会话密钥。之所以要3个是因为不能信任客户端的随机数生成逻辑,因为多数随机数生成是伪随机,或者说随机数的生成是有固定规律的,如果黑客掌握了这个规律,那他自己就能生成每次的密钥了。而3个随机数就能增大这个密钥的随机性。

看起来,方案很完美了,但实际上里面仍然存在一个漏洞:如果最开始下发的明文公钥就是假的呢?这会导致的结果是:用户全程和一个中间黑客通信:信任黑客的公钥,然后自以为安全的进行数据通信,结果把所有个人信息都暴露给黑客了,最重要的是,服务端对此一无所知!

因此,这个风险隐患太大了,如果哪天用户账户被盗找上门来,我们压根就知道发生了什么。

所以,客户端对于这个公钥要保持默认不信任态度,在拿到公钥后,首先就去校验这个公钥的真实合法性。

5. 公钥的合法性校验

为了公钥的合法性校验,人们建立了专门颁发证书的机构:"证书中心"(Certificate authority,简称CA),它会利用自己的私钥,将我们的公钥和相关信息进行加密,生成一个数字证书。在客户端内部,会存在一个受信任的根证书颁发机构,所以客户端在每次拿到一个公钥后,就去查询这个公钥是否在这里受信任证书列表中,如果不在,则认为公钥无效,不再进行后续操作。如果存在,再继续进行下一步认证。

至此为止,我们设计了一套麻雀虽小、五脏俱全的网络数据加密方案,而这也和Https的实现机制相近。

II. Https的弊端

Https的好处显而易见:对网络传输的数据进行加密,防止黑客监听、篡改,保证用户隐私数据安全。但是,对于大型应用而言,我们还需要关注它的弊端。

1. 2-RTT的延时

前面几篇文章中,我们一直在强调HTTP建立连接耗时对于客户端响应速度的重要性。一次普通的Http请求,除了真实数据传输的耗时,实际上很多耗时存在于TCP连接的三次握手(1.5个RTT)的耗时上。而对于一次Https的安全连接,则还需要花费时间在前期的会话密钥建立上,这里面存在1~2个RTT。

RTT (即 Round-Trip Time) ,数据往返时间的术语简称,数据在客户端、服务端来回一次,称为1个RTT。

2. 非对称加密CPU开销

非对称加密组件计算效率往往远低于对称加密组件,直接使用非对称加密组件加密业务数据,这样的性能损耗任何Server都是无法承受的。

也就是说,Https会导致每个网络请求都加上2个RTT的连接耗时。而且,这中间还存在一次非对称加密过程,这对于服务器CPU也存在较大的开销。

因此,Https对于存在大量短连接的App会造成不小的问题。下面,我们介绍一些常用的改进方案。

III. Https加速

既然我们已经知道Https的延时问题,那我们就必须要做一些优化来加快,否则虽然数据是加密了,但传输过程大大变慢了,导致用户体验严重破坏,反倒得不偿失。

top Created with Sketch.