本文谈谈我的看法,主要分为以下几个方面:
- HTTPS 协议是什么?
-
HTTPS 是如何工作的?
-
HTTPS 真的安全吗?
一、什么是 HTTPS
HTTPS,也称作 HTTP over TLS,TLS 前身是 SSL,会有各个版本。TLS 协议在 TCP/IP 协议栈中的关系如下:
上图描述了在 TCP/IP 协议栈中 TLS(各子协议)和 HTTP 的关系,HTTP+TLS 也就是 HTTPS。和 HTTP 相比,HTTPS 的优势:
- 数据完整性:内容传输经过完整性校验。
- 数据隐私性:内容经过对称加密,每个连接生成一个唯一的加密密钥。
- 身份认证:第三方无法伪造服务端(客户端)身份。
二、HTTPS如何工作的
1、工作流程
如下图所示,就是HTTPS协议的工作流程。
2、TCP三次握手
首先是TCP协议的三次握手,如下图所示:
- Step1:客户端发送一个SYN=1,ACK=0标志的数据包给服务端,请求进行连接,这是第一次握手;
- Step2:服务端收到请求并且允许连接的话,就会发送一个SYN=1,ACK=1标志的数据包给发送端,告诉它,可以通讯了,并且让客户端发送一个确认数据包,这是第二次握手;
- Step3:服务端发送一个SYN=0,ACK=1的数据包给客户端端,告诉它连接已被确认,这就是第三次握手。TCP连接建立,开始通讯。
我们通过wireshark抓包查看三次握手过程:
- Step1:启动wireshark抓包,打开浏览器输入www.huawei.com。
- Step2:使用ping www.huawei.com获取IP。
通过wireshark抓包,首先我们测试用www.huawei.com,先ping 网址得到IP。
- Step3:输入过滤条件获取待分析数据包列表 ip.addr == 113.96.132.64。
图中可以看到wireshark截获到了三次握手的三个数据包。第四个包才是HTTPS的Client Hello, 这说明HTTP的确是使用TCP建立连接的。
第一次握手数据包
客户端发送一个TCP,标志位为SYN,序列号为0, 代表客户端请求建立连接。 如下图。
数据包的关键属性如下:
- SYN :标志位,表示请求建立连接
- Seq = 0 :初始建立连接值为0,数据包的相对序列号从0开始,表示当前还没有发送数据
- Ack =0:初始建立连接值为0,已经收到包的数量,表示当前没有接收到数据
第二次握手的数据包
服务器发回确认包, 标志位为 SYN,ACK. 将确认序号(Acknowledgement Number)设置为客户的I S N加1以.即0+1=1, 如下图:
数据包的关键属性如下:
- [SYN + ACK]: 标志位,同意建立连接,并回送SYN+ACK。
- Seq = 0 :初始建立值为0,表示当前还没有发送数据
- Ack = 1:表示当前端成功接收的数据位数,虽然客户端没有发送任何有效数据,确认号还是被加1,因为包含SYN或FIN标志位。(并不会对有效数据的计数产生影响,因为含有SYN或FIN标志位的包并不携带有效数据)
第三次握手的数据包
客户端再次发送确认包(ACK) SYN标志位为0,ACK标志位为1.并且把服务器发来ACK的序号字段+1,放在确定字段中发送给对方.并且在数据段放写ISN的+1, 如下图:
数据包的关键属性如下:
- ACK :标志位,表示已经收到记录
- Seq = 1 :表示当前已经发送1个数据
- Ack = 1 : 表示当前端成功接收的数据位数,虽然服务端没有发送任何有效数据,确认号还是被加1,因为包含SYN或FIN标志位(并不会对有效数据的计数产生影响,因为含有SYN或FIN标志位的包并不携带有效数据)。
就这样通过了TCP三次握手,建立了连接。
3、HTTPS握手
3.1、客户端发起Client Hello握手
Client Hello握手
我们以抓博客园www.cnblogs.com为例,可以看到一个Client Hello握手,我们可以看到客户端握手发起了了一个握手类型为Client Hello的握手(Handshake Type:Client Hello),同时附带了这么一些信息:
- Random1(随机数)和一个时间戳。
- 客户端支持的加密协议套装。告诉 HTTPS 的服务器端,客户端能支持下面这 32 种加密协议套装中列出的算法,让服务器选择一个加密协议算法套装。
- 客户端支持的签名算法,让服务器端自由选择一个用于后续的加密通信。
- 访问的 Web 服务器的信息。
3.2、服务端收到Client Hello握手并发起Server Hello握手并下发证书
Server ACK回应
在客户端发送Client Hello握手后,服务端会回应ACK信息,如下图所示:
接下来HTTPS 服务器马上给客户端回复了下面这 4 条 SSL 握手信息。
- Server Hello
- Certificate
- Server Key Exchange Server
- Hello Done
Sever Hello握手
服务端也会发一串随机数Random2,并告诉客户端,服务器端准备选择TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256作为秘钥交互的加密协议套装,该加密协议的套装名字肯定出现在客户端发送给服务器的支持的 32 个列表中。
Certificate证书
SSL 服务器证书信息。在这条 HTTPS 服务器给客户端回复消息 SSL 握手信息里面,其还会把服务器端的 SSL 证书发送给客户端,从图中的转包信息中,我们能清晰地发现服务器端 SSL 证书的相关信息。
需要注意的是,如果是 SSL 的双向认证,服务器端也可以要求客户端把 SSL 证书发送给服务器端(对应的 SSL 握手消息名称为:CertificateRequest),这个时候,客户端就会把其 SSL 证书发送给服务器端,从而证明其就是服务器端信任的客户端。
Server Key Exchange Server以及Server Hello Done
如下图所示,HTTPS 服务器告诉了客户端其将会采用的 EC Diffie-Hellman 算法进行 HTTPS 服务器和客户端的秘钥交换,并提供了 EC Diffie-Hellman 算法使用到的服务器端的参数:
- 曲线类型:named_curve: x25519
- 公钥信息:Pubkey
- 签名的算法:rsa_pkcs1_sha256
- 签名的信息:signature
3.3、客户端完成密钥交换并发起加密传输请求
当客户端收到服务器端的相关公钥信息、SSL 证书以及摘要算法和摘要信息后,会发送一个ack告诉服务端已经收到hello数据包以及证书。
此时客户端需要验证服务器发过来的证书的合法性:通过操作系统内置的ca证书,来验证服务器证书的颁发者和过期时间是否合法,如果存在数据签名,则用操作系统中颁发者CA的公钥对签名解密,用相同的hash算法算出签名的hash值,将这个hash值与签名中的hash值对比。验证是否是真实的服务器发送的。然后客户端会响应下面3 条 SSL 握手信息:
- Client Key Exchange
- Change Cipher Spec
- Encrypted Handshake Message
Client Key Exchange 握手信息
客户端验证通过,取出证书中的公钥,然后再生成一个随机数random3,用这个公钥对random3加密,生成PreMaster Key,客户端根据服务器传来的公钥生成了 PreMaster Key,Client Key Exchange 就是将这个 key 传给服务端,服务端再用自己的私钥解出这个 PreMaster Key 得到客户端生成的 Random3。至此,客户端和服务端都拥有 Random1 + Random2 + Random3,两边再根据同样的算法就可以生成一份秘钥,握手结束后的应用层数据都是使用这个秘钥进行对称加密。为什么要使用三个随机数呢?这是因为 SSL/TLS 握手过程的数据都是明文传输的,并且多个随机数种子来生成秘钥不容易被暴力破解出来。
根据PreMaster Key,服务器和客户端会计算出相同的主密码(Master secret),然后根据主密码生成下面的比特序列(秘钥素材)。
- 对称密码的秘钥
- 消息认证码的秘钥
- 对称密码的 CBC 模式中使用的初始化向量(IV)
需要注意的是,Client 秘钥交换的方式主要有两种:
- 通过 RSA 公钥密码交换秘钥:这个时候客户端会在发送 ClientKeyExchange 消息时,将经过加密的预备主密码一起发送给服务器。
- 使用 Diffie-Hellman 交换秘钥:客户端会在发送 ClientKeyExchange 消息时,将 Diffie-Hellman 公开值(Pub Key)一起发送给服务器,根据这个值,客户端和服务器会各自生成预备主密码,而且更加这个预备主密码能够生成相同的对称主密码。
Change Cipher Spec
Change Cipher Spec握手信息就是告诉服务端,要换密码了。
Encrypted Handshake Message
客户端发出使用主密码加密的结束信息,告诉服务器端:“秘钥交换握手协议到此结束”。
3.4、服务端也告知客户端进行加密传输
然后HTTPS 服务端马上给客户端回复 2 条 SSL 握手信息,这时轮到服务器端发送“Change Cipher Spec”消息了,服务器告诉客户端:“好,现在我也要切换密码了”。服务器端用预备主密码(Pre-Master secret)计算出的主秘钥加密了一条信息,并发送给客户端:“好的,秘钥交换握手协议到此结束”。如果通信双方都能把结束消息解密成功,说明主秘钥已经交换成功。就可以发送真正的用主密码加密的应用数据的信息了。
3.5、开始进行数据加密传输
服务端就可以用对称秘钥把加密过的 HTML 网页内容发送给客户端了,如下图所示:
上面的步骤只是把我当前环境下抓取到的使用 TL S1.2 协议规范进行了 HTTPS 通信原理和过程的梳理和解释,在不同的环境下,其通信过程会有一些差异,比如,如果配置了双向 SSL 认证,其 SSL 服务器端还会要求客户端把客户端的证书发送到服务端,从而验证客户端是否是可信任的,另外在进行主密码交换的过程中,也可能采用 RSA 公钥密码,而不是 Diffie-Hellman,此时,其 SSL 握手消息会有所不同,但是整体的流程和交互过程思路基本上保持相同。
三、HTTPS真的安全么
HTTPS 真的完全安全吗?连访问的域名都获取不到?答案是否定的。上述 HTTPS 在握手阶段有一个很重要的东西:证书。
1、SNI:域名裸奔
当访问 HTTPS 站点时,会首先与服务器建立 SSL 连接,第一步就是请求服务器的证书。
当一个 Server IP 只对应一个域名(站点)时,很方便,任意客户端请求过来,无脑返回该域名(服务)对应的证书即可。但 IP 地址(IPv4)是有限的呀,多个域名复用同一个 IP 地址的时候怎么办?服务器在发送证书时,不知道浏览器访问的是哪个域名,所以不能根据不同域名发送不同的证书。因此 TLS 协议升级了,多了 SNI 这个东西,SNI 即 Server Name Indication,是为了解决一个服务器使用多个域名和证书的 SSL/TLS 扩展。现在主流客户端都支持这个协议的。
它的原理是:在与服务器建立 SSL 连接之前,先发送要访问站点的域名(Hostname),这样服务器会根据这个域名返回一个合适的证书。此时还没有办法进行加解密,因此至少这个域名是裸奔的。如下图所示,访问博客园在进行Client Hello握手时就会带上需要访问的域名:
2、中间人攻击
HTTPS 中的关键其实在于这个证书。中间人攻击就是在客户端、服务器之间多了个『中介』,『中介』在客户端、服务器双方中伪装对方。如下所示这个『MitmProxy』充当了中间人:
然后访问网站时,会出现浏览器的警告:
提示,连接不是私密连接,其实就是浏览器识别了证书不太对劲,没有信任。而如果此时你信任浏览器,则可以正常访问。因此,当你信任证书后,在中间人面前,又是一览无余了。
因此,一般去申请ca证书的时候,选择正规的CA机构进行证书申请。