HTTPS

1,019 阅读13分钟

为什么需要 HTTPS

一言以蔽之:HTTP 协议不安全。HTTP 明文传播的特性使其很容易被“中间人”在链路中截获、修改、伪造请求。一个安全的通信协议,应当确保四个要素:

  1. 机密性:数据只能被可信的人访问
  2. 完整性:数据在传输过程中不能被篡改
  3. 身份认证:确认通信双方的身份
  4. 不可否认:记录通信过程

为了满足以上需求,HTTPS 应运而生。

什么是 HTTPS

超文本传输安全协议(Hypertext Transfer Protocol Secure,简称:HTTPS)是一种通过计算机网络进行安全通信的传输协议,默认端口号为 443。从 rfc2818 的标题 “HTTP Over TLS” 也可以看出,它只是在 TCP/IP 与 HTTP 之间加了一层 TLS:

image.png

因此可以说:HTTPS 的安全性是由 TLS 保证的。

SSL/TLS

SSL 即安全套接层(Secure Socket Layer),在 OSI 模型中位于第五层(会话层),互联网工程组 IETF 在1999年将其更名为安全传输层协议 TLS(Transport Layer Security),故 TSL1.0 实际上就是 SSLv3.1。TLS 最新版本是2018年的 1.3,广泛使用的是1.2。

浏览器和服务器在使用 TLS 建立连接时需要选择一套恰当的加密算法实现安全通信,这些算法的组合称为为“密码套件”(cipher suit),其基本格式为“密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法”,如“ECDHE-RSA-AES256-GCM-SHA384” 表示“握手时使用 ECDHE 算法进行密钥交换,用 RSA 签名和身份认证,握手后的通信使用 AES 对称算法,密钥长度为256位,分组模式是 GCM 摘要算法 SHA384 用于消息认证和产生随机数”。

原理

对称加密

对称加密顾名思义,指加密和解密用的密钥相同:

image.png

目前常用的算法是 AES 和 ChaCha20。其中 AES 的密钥长度可以是 128、192 或 256,ChaCha20 密钥长度固定为 256。需要注意的是,密钥的长度单位是位(bit),而非字节(byte),比如说密钥长度为 128,就是 16 字节的二进制串。

由于一个密钥只用用来加密固定长度的明文,因此对称加密还需要对明文按固定长度做分组,来达到只用一个一个密钥加密任意大小明文的目的,常用的分组模式有 CBC(计算器模式)、CTR(密文分组链接模式)等。

如果只有对称加密显然是不够的,因为即使明文加密了,对称密钥还是需要通过网络传输,如果中途被截获,那么加密过程就毫无意义了。

非对称加密

非对称加密顾名思义,指加密和解密用的密钥不同:

image.png

公钥是公开的,而私钥是保密的。采用公钥加密后,只能用对应的私钥解密;相应的,采用私钥加密后,只能用对应的公钥解密。

非对称加密常用算法有 RSA、ECC、DH 等。其中 RSA 的安全性基于"整数分解难题”,一般长度为 1024 位,重要场合则为 2048 位。ECC 的安全性基于“椭圆曲线离散对数难题”,其子算法 ECDHE 用于密钥交换,ESDSA 用于数字签名。相比 RSA,ECC 在安全和性能上都有明显的优势,160位的 ECC 相当于 1024 位的 RSA。DH算法数学基础是离散对数,它没有原文、摘要这些参数,因此只能用于密钥交换。

混合加密

虽然非对称加密看起来很完美,但是其计算量过大,运算速度很慢,不适合直接用来加密明文。因此,可以采取“混合加密”的方案:先使用非对称加密算法,获得公钥和私钥,用随机数生成对称加密算法使用的“会话秘钥”,再用公钥加密该会话密钥后发送给对方,对方拿到后用私钥解密,取出会话密钥,之后便可以通过该会话密钥对称加密通信。对称加密密钥很短,解决了非对称加密运算时间长的问题。

利用混合加密,已经可以确保通信的机密性,但尚未确保完整性,也没有身份认证。因此,想要达成安全通信还需要进一步扩充。

摘要算法

摘要算法即哈希函数,是一种单向不可逆的算法,它把任意长度的数据映射成固定长度且独一无二的字符串,而且它对输入的变化极为敏感,任何数据的修改都会改变输出的结果。因此,它可以作为判断数据是否修改的工具。

image.png

常用的摘要算法有 MD5、SHA-1 等,但它们因为安全强度不够已被 TLS 禁用,目前 TLS 推荐使用的摘要算法是 SHA-2,其下又可再分为六个不同的算法标准,包括了:SHA-224、SHA-256、SHA-384、SHA-512、SHA-512/224、SHA-512/256,后面的数字指的是生成摘要的长度。

利用摘要算法的特性,把明文和摘要一并加密之后再发送,接收方收到后再计算明文的摘要并与发送方发送的摘要作比对,如果没有变化则说明明文没有被修改,如此义来便确保了通信的完整性:

image.png

数字签名与证书

利用非对称加密中私钥只有自己拥有的特性,可以把其看做是一种独一无二的身份信息。发送前用私钥加密明文的摘要,作为数字签名,接收方收到后用公钥解密拿到摘要后,对比原文验证完整性,就实现了身份认证。因此只要通信双方交换公钥,互相“签名”、“验签”,就可以达到身份认证的目的。

image.png

但是这种方式还有一个漏洞,就是谁都可以发布公钥,无法确认获得的公钥的安全性。因此还需要“第三方”来对公钥的安全做个认证,即 CA(Certificate Authority,证书认证机构)。

申请证书要在公钥的基础上加上申请人的信息比如我是谁,来自哪里,名字叫什么,证书适用于什么场景等,然后带上自己的签名发送给 CA。CA 收到申请文件后如果同意颁发,会用 CA 的私钥对申请文件签名,如果是用自己的私钥给自己的公钥签名,就叫自签名。

签名后的证书包含申请者的基本信息,CA 的基本信息,证书的使用年限,申请人的公钥,签名用到的摘要算法以及 CA 的签名。

那么如何验证证书呢?以浏览器为例,浏览器收到证书后,查看是哪个 CA 签名的这个证书,在自己信任的 CA 库中,找到相应的证书,用 CA 证书里的公钥解密网站证书上的签名,再用证书给定的摘要算法算出网站证书的摘要,如果相同则说明证书合法。

可是谁来保证 CA 的合法性呢?一般 CA 都是分等级的,CA 的证书由上一级 CA 签名,最高级的证书是根证书(Root CA),它的证书是自签名证书,必须信任它,不然整个证书信任链就走不下去了。通常情况,操作系统和浏览器都会内置各大 CA 的根证书。

image.png

TLS1.2

握手过程

目前主流的 TLS 握手过程是 ECDHE 握手过程:

  1. 建立 TCP 连接后,浏览器向服务器发送 Client Hello,内容包含客户端支持的 TLS 版本号(Version)、支持的密码套件列表(Cipher Suites)、以及一个用于后续生成会话密钥的随机数(Client Random),如图:

image.png

  1. 服务器收到后,会一次打包发送四个模块:

image.png

  • Server Hello:确认客户端发来的 TLS 版本号(Version),从客户端提供的密码套件列表中选择一个作为本次通信的密码套件(Cipher Suite),并返回一个用于后续生成会话密钥的随机数(Server Random):

image.png

  • Certificate:显然是服务器的证书:

image.png

  • Server Key Exchange:包含 Server 密钥协商需要的参数(Server Params),此处是椭圆曲线的公钥,以及对整个消息的签名。注意:签名采用的私钥是证书的私钥,从下图可以看出这里采用的签名算法是 RSA。

image.png

  • Server Hello Done:表示服务器需要发送的内容结束了:

image.png

  1. 客户端收到后,先验证服务器的证书与签名,再按照服务器选择的密码套件的要求,使用对应的算法生成Client密钥协商需要的参数(Client Params),此处是椭圆曲线的公钥:

image.png

根据 Client Params、Server Params,就可以通过 ECDHE 算法计算出预备主密钥(Pre Master Secret)。然后,将计算出的 “pre Master Secret”、交换得到的随机数 “Client Random” 和 “Server Random” 作为 PRF 算法的参数,生成主密钥 “Master Secret”。再将 “Master Secret”、“Client Random”、“Server Random” 作为 PRF 算法的参数,生成会话密钥,也称为密钥块(key_block)。

会话密钥可以有多个,比如客户端发送消息用 “client_write_key”、服务器发送消息用 “server_write_key”。因为双方已经共享了 “Master Secret”, 所以两边可以一致地生成多个密钥,使用对称加密。

本次客户端发送给服务器的 TCP 包包含三个模块:

  • Client Key Change:包含 Client Params:

image.png

  • Change Cipher Spec Protocol:事实上,此记录类型不属于握手消息,它非常简单,就是一个“通知”,告诉对方,后续的数据都将使用加密保护。那么反过来,在它之前,数据都是明文的。

    一个 Finished 消息会在一个 change cipher spec 消息后立即发送,以证明密钥交换和认证过程是成功的

image.png

  • Encrypted Hand Shake Message:包括了从 Client Hello 开始一直到(但不包括)Finished 消息的所有握手消息:

image.png

注意:如果之前服务器发出了 “Certificate Request” 消息,那么客户端还需要额外发送自己的证书以及签名。这样的TLS过程称为双向认证。然而客户端的数量太多,如果给每个客户端都发证书显然不现实。因此通常情况下单向认证足矣,服务器可以要求客户端提供用户名和密码来验证身份,这也是我们熟知的模式。

  1. 服务器收到后,发 “Change Cipher Spec” 和 “Finished” 消息,双方都验证密钥交换和认证过程成功,握手正式结束,后面就收发被加密的 HTTP 请求和响应了。

使用 ECDHE 握手全程的流程图如下:

ECDHE握手

而 RSA 握手有略微不同。

  1. 对于 RSA 加密套件,Client 不需要额外参数就可以计算出预备主密钥(Pre Master Secret),然后使用服务器的公钥加密发送给服务器,因此不需要服务器发送 Server Key Exchange 就可以完成密钥协商,另外,ECDHE在 Server Key Exchange 中签名, RSA 握手因为没有 Server Key Exchange 消息,签名会改为在 Server Hello 中。

这样存在的问题就是,如果服务器的私钥被黑客拿到,那么他就可以解密出之前所有报文的预备主密钥。相比之下,ECDHE 握手中,没有直接采用服务器的公钥来加密数据,而是交换两端的椭圆曲线公钥(Client Parms 与 Server Params)来保证预备主密钥的安全性。

  1. 使用 RSA 握手,客户端必须等到接收到服务器的 “Finished” 才能发送 HTTP 报文。而使用 ECDHE 握手,可以在服务器尚未响应时就发送 HTTP 报文,这样就节省了 1RTT 的时间。你可能会问为什么 RSA 不可以这样,我也不知道,找了很久都没找到结果... 期待评论区大神解惑。

其流程图如下:

image.png

TLS1.3

TLS1.3 对 TLS 的安全性做了一次升级,它明确禁止在记录协议里使用压缩;把伪随机数函数 PRF 升级为 HKDF;同时废除了诸多加密算法,最终只保留了五个密码套件:

image.png

为了向下兼容,TLS1.3 没有改变现有的记录格式,而是选择在记录末尾增加扩展协议(Extension Protocol),老版本的 TLS 不支持扩展协议,直接忽略即可。需要注意的是,记录协议的 Version 字段仍是 TLS1.2,需要在 Hello 消息后面加上扩展 “supported_version” 来说明使用新版本的协议。

握手过程

利用扩展协议,可以对 HTTPS 做各种针对性的优化。TLS1.3 握手过程中,客户端会在 Client Hello 使用扩展 “supported_groups” 带上支持的椭圆曲线,用 “key_share” 带上曲线对应的客户端公钥参数,用 “signature_algorithms” 带上签名算法。

服务器收到后在这些扩展中选择一个曲线和参数,再用 “key_share” 扩展返回服务器的公钥参数,就实现了双方的密钥交换。后续的部分就和TLS1.2相同了。

其流程图如下:

image.png

需要注意的是,计算出主密钥后,服务器会立刻发出 “Change Cypher Spec” 开启加密通信,因此后续发给客户端的证书等信息都会加密后再发送,进一步增加了安全性。

HTTPS 优化

首先,应当尽量采用 TLS1.3,节省握手时间,也更加安全。如果只能使用 TLS1.2,也应该使用椭圆曲线的 ECDHE 算法。

此外还可以使用会话复用,原理类似 HTTP 缓存,对于提升性能十分有效。

会话复用分两种,第一种方案是 Session ID,客户端和服务器首次连接后各自保存一个会话的 ID 号,并在内存存储主密钥和其他相关的信息。当客户端再连接时发个 ID 过来,服务器就在内存里查询,如果查询到就直接使用对应的主密钥恢复会话状态,跳过证书验证和密钥交换,只用一个 RTT 就可以建立安全通信。如图:

image.png

Session ID 是应用最广的方案,但它要求服务器保存每个客户端的会话数据,当数据过多时,无疑会加重服务器的负担。因此诞生了第二种方案 Session Ticket, 它有点类似 HTTP 的 Cookie,其实就是把服务器存储会话数据的压力分担给了客户端。服务器会加密会话信息,用 “New Session Ticket” 消息发给客户端,客户端将其保存,再次重连的时候,会用扩展 “session_ticket” 发送 “Ticket" 给服务器,经服务器解密验证后,就可以恢复会话开始加密通信。


流程图来源:

透视http协议

参考资料:

透视http协议

趣谈网络协议

HTTPS温故而知新

SSL/TLS及证书概述

维基百科-分组密码工作模式

rfc2818

rfc5246

rfc8422