在浏览器与Facebook服务器进行TLS握手过程中(现在称为TLS握手,假设已完成TCP三次握手):
首先,双方协商连接使用的协议:第一次”往”:客户端在一个名为Client Hello的TLS握手包中发送客户端运行的TLS协议版本和支持的加密套件列表至服务器端。
然后,客户端验证服务器:第一次”返”:服务器在一个名为Server Hello的TLS握手包中发送选取的密钥交换算法和自己的数字证书公钥(或证书链),以及一个名为Server Hello Done的TLS握手包。客户端遍历本地已导入的CA证书,尝试验证服务器的证书是否由声称的CA机构签发。验证失败时,提示用户该服务器证书不可信,并询问是否手动设置信任关系或拒绝。验证成功时,不显示任何信息。
然后,服务器验证客户端:(如果上述验证失败,则中断TLS握手)第二次”往”:客户端使用之前协商的密钥交换算法生成一个随机密钥,用于会话的加密。然后,客户端使用服务器的证书公钥加密该会话密钥,并通过Change Cipher Spec Protocol,在一个名为Client Key Exchange的TLS握手包中发送至服务器端。(浏览器可能会将加密密钥的时间开销误认为是引入HTTPS数据传输延迟,因为在收到响应数据并解密前,整个页面不可见)
第二次”返”:服务器使用自己的证书私钥解密会话密钥。解密失败说明客户端身份不可信。如果解密成功,说明客户端是合法的。服务器在一个名为Change Cipher Spec Protocol的TLS记录层中包含Finished握手包,返回给客户端,表明客户端身份通过验证。客户端收到通知后,开始使用协商好的会话密钥来加密并发送HTTP请求。服务器使用相同的会话密钥解密并读取HTTP请求,加密并返回HTTP响应,浏览器使用会话密钥解密并读取HTTP响应…如此反复,构成完整的HTTPS通信流量。
在早期,客户端浏览器进程和服务器端web服务器进程控制CPU加解密HTTPS流量时,会造成一定的计算时间开销,并引入额外的网络传输延迟。但现在的客户端和服务器端应用的并发模型高效利用多核、高速CPU的计算优势,这些延迟大多不太明显。总结一下,TCP三次握手需要一次”往返”,TLS握手协商交换密钥、验证双方身份需要两次”往返”,因此在实际开始传输应用层数据(请求和响应页面)前,需要等待三次往返造成的时间开销。早期的时间开销更多取决于CPU计算能力和客户/服务应用的编程模型,现在更多取决于客户和服务器端的物理距离、骨干网络带宽和当前负载,以及是否采用CDN内容分发网络的加速访问技术等因素。TLS握手结束后,即开始传输应用数据。此时,浏览器根据用户提交的内容生成HTTP请求,其内置的SSL/TLS模块将HTTP请求作为有效载荷封装在名为”TLS记录层”的数据包中。每个数据包大小上限为16KB,通常一个HTTP请求的内容远小于16KB,因此可以封装在一个大小约为1.4KB的TLS记录层中。还可以对请求或响应数据进行压缩(可选),并为该TLS记录层计算并添加最多32字节的信息验证码(MAC,类似于校验和,必选)。服务器端接收并解密TLS记录层中的数据后,计算其中的MAC,将计算结果与记录层中携带的MAC字段值对比,验证该层数据的完整性和可靠性,防止TLS记录层在传输过程中数据丢失或被篡改。浏览器的SSL/TLS模块使用之前协商好并生成的随机密钥来加密这个TLS记录层数据包,传递给操作系统的TCP/IP协议栈,后者依次添加TCP头部成为TCP分段,添加IP头部成为IP分组,最后封装成一个大小约1.5KB的以太网帧,然后传递给服务器端。(1.5KB扣掉20字节的IPv4头部、20字节的TCP头部、40字节的TCP选项,等于1.42KB,这是常见的单个TLS记录层数据包大小,超过这个大小的TLS记录层数据包需要封装成多个以太网帧,准则是不超过链路层MTU上限)
服务器端操作系统的TCP/IP协议栈以相反顺序解封装从链路层到传输层的头部,然后将裸露出来的TLS记录层数据包交给web服务器的OpenSSL模块进行解密。解密成功后,将HTTP请求数据交给web服务器的请求处理与响应模块,后者以类似浏览器构建请求的步骤将HTTP响应作为有效载荷封装在TLS记录层中传递。但是,对于大型HTML页面或流媒体等HTTP响应体,其数据量往往超过每个TLS记录层的16KB上限。例如,YouTube要通过HTTPS传输加密的1.6MB的视频流数据至客户端,必须将其划分为100个16KB的TLS记录层数据包,每个数据包中的流媒体片段作为有效载荷,占据了15KB以上的空间,剩余的几十字节才是TLS记录层数据包本身的各种头部字段,例如记录层类型、版本、MAC等字段。同样地,每个TLS记录层数据包在到达传输层后,被分割成约10~13个大小约为1.4KB的TCP分段。最终结果是,产生1000多个以太网帧来传输这1.6MB的视频内容。关键是,每个满载的TLS记录层数据包被分割成10几个TCP分段,浏览器必须接收到属于同一个TLS记录层数据包的所有TCP分段后,才能解密数据并还原其中的视频片段。然而,这些TCP分段在因特网上传输时难免会有传输延迟,到达客户端的时间顺序不一致,数据损坏以及丢失的情况发生。这都会导致浏览器等待服务器端重传TCP分段而延迟缓冲视频的播放。可以说,相较于普通的HTTP视频站点,使用HTTPS提供视频服务的站点的性能更依赖于整个因特网的稳定性,以及地区性骨干网的带宽和当前负载等因素。对于提供非视频流服务的HTTPS站点而言,通常可以在一个TCP分组中封装完整的TLS记录层数据包,而不是由多个TCP分组构成一个完整的TLS记录层数据包。
最后,Facebook在HTTPS方面的安全配置做得最好。例如,我们安装了BurpSuite开发公司的SSL证书(PostSwigger CA)作为本地操作系统上的”受信任的根证书颁发机构”:
原则上,BurpSuite将在浏览器与服务器之间充当SSL代理。当浏览器与Facebook进行TLS握手时,BurpSuite拦截浏览器的TLS client hello握手包,并修改一些密钥交换参数(可以在BurpSuite中配置密钥交换算法和TLS版本)。然后,BurpSuite转发TLS client hello握手包到Facebook服务器,冒充浏览器与服务器协商加密参数并进行密钥交换。这样,BurpSuite可以解密和显示与Facebook的加密流量。BurpSuite接收到服务器发送的证书后,使用自己的PostSwigger CA根证书对Facebook的服务器证书进行”重签名”。这样,原本签发Facebook证书的证书颁发机构变成了PostSwigger CA。然后,BurpSuite将Certificate握手包转发给客户端。这样,浏览器将BurpSuite视为Facebook服务器(接受证书),无顾虑地与其进行HTTPS通信。这样,BurpSuite就能够在中间透明地解密和转发HTTPS流量,并向用户显示解密后的信息。这就是SSL代理能够”剥掉”SSL流量的原因。对于普通的HTTPS站点,这一招有效。但对于Facebook来说无效,因为Facebook使用了”HTTPS严格传输安全”(HTTP Strict Transport Security,HSTS),强制要求浏览器使用HTTPS与其通信,不能忽略有问题的证书。实际上,Chrome和Firefox都不会接受BurpSuite返回的伪造证书,拒绝与其建立HTTPS连接,并遵循HSTS规范,禁止用户忽略有问题的证书。唯有IE例外,它仍然提供忽略选项,甚至最新版本也是如此。这给我们使用BurpSuite欺骗IE提供了机会,最终BurpSuite可以捕获并解密IE与Facebook之间的HTTPS流量,取代了Wireshark的任务。
最后,使用BurpSuite拦截IE与Facebook站点之间的HTTPS流量时,我们可以实时修改Facebook返回的HTTPS响应,去掉其中的Strict-Transport-Security响应头部及其值,然后转发给浏览器,这样可以观察IE对该响应头的处理方式。