目录
一、HTTP基本概念
二、GET 与 POST
2.1、GET 与 POST 有什么区别?
2.2、GET 和 POST 方法都是安全和幂等的吗?
三、HTTP 缓存
3.1、强制缓存:
3.2、协商缓存
四、HTTP 特性
4.1、HTTP/1.1
4.1.1、HTTP/1.1 的优点
4.1.2、HTTP/1.1 的缺点
4.1.3、HTTP/1.1 的性能
五、HTTP 与 HTTPS
5.1、HTTP 与 HTTPS 的区别
5.2、HTTPS 解决了 HTTP 的哪些问题呢?
5.3、HTTPS 是如何建立连接的?
5.4、HTTPS 的应用数据是如何保证完整性的?
5.5、HTTPS 就一定安全可靠吗?
5.6、为什么抓包工具能够截取 HTTPS 数据?
六、HTTP/1.1、HTTP/2、HTTP/3 的演变
6.1、HTTP/1.1 相比 HTTP/1.0 提高了什么性能?
6.2、HTTP/2 的优化
6.3、HTTP/2 的缺陷
6.4、HTTP/3 做了哪些优化?
七、HTTP/1.1 如何优化?
7.1、避免发送 HTTP 请求
7.2、如何减少 HTTP 请求次数?
7.2.1、减少重定向次数:
7.2.2、和并请求
7.2.3、延迟发送请求
7.3、如何减少 HTTP 响应的数据大小
八、HTTPS RSA 握手解析
RSA 握手握手过程:
TLS 第一次握手:
TLS 第二次握手:
TLS 第三次握手:
TLS 第四次握手
RSA 算法的缺陷
九、优化 HTTPS
一、HTTP基本概念
HTTP(HyperText Transfer Protocol):是一个在计算机世界里专门在两点之间传输文字、图片、音频、视频等超文本数据的约定和规范。
HTTP常见的状态码有:
HTTP 常见的字段:
- Host 字段:客户端发送请求时,用来指定服务器的域名
- Content-Length 字段:服务器在返回数据时,会有 Content-Length 字段,表明本次回应的数据长度(HTTP 协议通过设置回车符,换行符作为 HTTP header 的边界,通过 Content-Length 字段作为 HTTP body 的边界,这两个方式都是为了解决”粘包“的问题)
- Connection 字段:最常用于客户端要求服务端使用【HTTP 长连接】机制,以便其他请求复用,它的特点就是只要任意一端没有明确提出断开连接,则保持 TCP 连接状态
- Content-Type 字段:用于服务器回应时,告诉客户端,本次数据是什么格式
- Content-Encoding 字段:说明数据的压缩方法,表示服务器返回的数据使用了什么压缩格式
二、GET 与 POST
2.1、GET 与 POST 有什么区别?
- GET 可以存在 Cache 中,POST 不可以
- GET 请求在 URL 中传递的参数是有长度限制的,POST 对长度没有限制
- GET 参数通过 URL 传递,POST 放在 RequestBody 中
- GET 请求参数会完整的保留在浏览器的历史记录中,POST 请求的参数不会保留
- 理论上 POST 要比 GET 安全,毕竟传输参数时 URL 不可见
- GET 会产生一个 TCP 数据包,POST 产生两个 TCP 数据包
- 对于 GET 请求,浏览器会把 HTTP Header 和 Data 一起发送出去,服务器响应 200,请求成功
- 对于 POST 请求,浏览器会先发送 Header ,服务器会响应 100(已经收到请求的第一部分,正在等待其余部分),浏览器再次发送 Data ,服务器返回 200,请求成功。但并不是所有的浏览器都会在 POST 中发送两次包,FireFox 就只发送一次
2.2、GET 和 POST 方法都是安全和幂等的吗?
安全和幂等的概念:
- 在 HTTP 协议里,安全是指请求方法不会破坏服务器上的资源
- 幂等意思是多次执行相同的操作,结果都是相同的
如果从规范定义来看:
- GET 方法就是安全且幂等的,因为它是只读操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。所以可以对 GET 请求的数据做缓存,这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上,而且在浏览器中 GET 请求可以保存为书签
- POST 因为是新增或者提交数据的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。所以,浏览器一般不会缓存 POST 请求,也不能把 POST 请求保存为书签
三、HTTP 缓存
避免发送 HTTP 请求的方法就是通过缓存技术,HTTP 设计者早在之前就考虑到了这点,因此 HTTP 协议的头部有不少是针对缓存的字段,HTTP 缓存有两种实现方式,分别是强制缓存和协商缓存。
3.1、强制缓存:
强缓存是指只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在浏览器这边。
强缓存是依赖下面两个 HTTP 响应头部(Response Header)字段实现的,都用来表示资源在客户端缓存的有效期:
- Cache-Control,是一个相对时间
- Expires,是一个绝对时间
如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control 的优先级高于 Expires。
Cache-Control 选项更多一些,设置更加精密,所以建议使用 Cache-Control 来实现强缓存,流程如下:
- 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 Cache-Control ,Cache-Control 中设置了过期时间的大小
- 浏览器再次请求访问服务器中的该资源时,会先通过请求资源的时间与 Cache-Control 中设置的过期时间大小,来计算出该资源是否过期,如果没有,则使用该缓存,都则重新请求服务器
- 服务器再次收到请求之后,会再次更新 Response 头部的 Cache-Control
3.2、协商缓存
当我们在浏览器使用开发者工具时,可能会看到某些请求的响应码是 304 ,这个是告诉浏览器可以使用本地缓存的资源,通常这种通过服务端告知客户端是否可以使用缓存的方式被称为协商缓存。
协商缓存可以基于两种头部来实现:
第一种:请求头部的 If-Modified-Since 字段与响应头部中的 Last-Modified 字段实现,这两个字段的意思是:
响应头部中的 Last-Modified :标示这个响应资源的最后修改时间
请求头部中的 If-Modified-Since :当资源过期了,发现响应头中具有 Last-Modified 声明,则再次发起请求的时候带上 Last-Modified 的时间,服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间进行对比(Last-Modified),如果最后修改时间较新(大),说明资源又被修改过,则返回最新资源,HTTP 200 OK;如果最后修改时间较旧(小),说明资源无新修改,响应 HTTP 304 走缓存。
第二种:请求头部中的 If-None-Match 字段与响应头部中的 ETag 字段,这两个字段的意思是:
响应头部中 ETag :唯一标识响应资源
请求头部中的 If-None-Match:当资源过期时,浏览器发现响应头中有 ETag,则再次向服务器发起请求时,会将请求头 If-None-Match 值设置为 ETag 的值。服务器收到请求后进行比对,如果资源没有变化返回 304,如果资源变化了返回 200。
第一种方式是基于时间实现的,第二种方式是基于一个唯一标识实现的,相对于后者可以更加准确地判断文件内容是否被修改,避免由于时间篡改导致的不可靠的问题。
如果第一次请求资源的时候,服务端返回的 HTTP 响应头部同时有 ETag 和 Last-Modified 字段,那么客户端再下一次请求的时候,如果带上了 ETag 和 Last-Modified 字段信息给服务端,这时 ETag 的优先级更高,也就是服务端会判断 ETag 是否变化了,如果 ETag 有变化则不用在判断 Last-Modified 了,如果 ETag 没有变化,然后再看 Last-Modified。
为什么 ETag 的优先级更高呢?这是因为它主要能解决 Last-Modified 几个比较难的问题:
- 在没有修改文件内容情况下文件的最后修改时间可能也会有变化,这会导致客户端认为这文件被改动了,从而重新请求
- 可能有些文件是在秒级以内修改的,If-Modified-Since 能检查到的粒度是秒级的,使用 ETag 就能够保证在这种需求下客户端在 1 秒内能够刷新多少次
- 有些服务器不能精确获取文件的最后修改信息
注意:协商缓存这两个字段都需要配合强制缓存中 Cache-Control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求。
当使用 ETag 字段实现的协商缓存的过程:
- 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的
- 当浏览器再次请求访问服务器中的该资源时,首先会检查强制缓存是否过期:
- 如果没有过期,则直接使用本地缓存
- 如果过期了,会在 Response 头部加上 If-None-Match 字段,该字段的值就是 ETag 唯一标识
- 服务器再次收到请求后,会根据请求中的 If-None-Match 值与当前请求的资源生成唯一标识进行比较:
- 如果值相等,则返回 304 Not Modified ,不会返回资源
- 如果不相等,则返回 200 状态码和返回资源,并在 Response 头部加上新的 ETag 唯一标识
- 如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源
四、HTTP 特性
4.1、HTTP/1.1
4.1.1、HTTP/1.1 的优点
①、简单
HTTP 基本的报文格式就是 header + body ,头部信息也是 key-value 简单文本的形式,易于理解,降低了学习和使用的门槛
②、灵活和易于拓展
HTTP 协议中的各类请求方法、URI/URL 、状态码、头字段等每个组成要求都没有被固定死,都允许开发人员自定义和扩充。同时由于 HTTP 是工作在应用层,则它下层可以随意变化,比如:
HTTPS 就是在 HTTP 与 TCP 层之间增加了 SSL/TLS 安全传输层
HTTP/1.1 和 HTTP/2.0 传输协议使用的是 TCP 协议,而到了 HTTP/3.0传输协议改用了 UDP 协议
③、应用广泛和跨平台
4.1.2、HTTP/1.1 的缺点
①、无状态双刃剑
无状态的好处就是服务器不会记忆 HTTP 的状态,所以不需要额外的资源来记录状态信息,这能减轻服务器的负担,能够把更多的 CPU 和内存用来对外提供服务。
坏处就是,既然服务器没有记忆能力,它在完成有关联性的操作时会非常麻烦,每操作一次就要验证一次,对于无状态的问题,解决方法比较简单的方式就有 Cookie 技术。Cookie 通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态,相当于,在客户端第一次请求之后,服务器会发下一个装有客户信息的【小贴纸】,后续客户端请求服务器的时候,带上【小贴纸】,服务器就能够认得了。
②、明文传输双刃剑
明文传输意味着在传输过程中的信息,是可方便阅读的,比如抓包都可以直接肉眼查看,为我们调式工作带来了极大的遍历。但正因为如此,HTTP 的所有信息都暴露在外,相当于信息裸奔。在传输的过程之中,信息的内容都毫无隐私可言,很容易被窃取。
③、不安全
- 通信使用明文传输,内容可能被监听
- 不验证通信方的身份,有可能遭遇伪装
- 无法访问报文的完整性,所以有可能已遭篡改
4.1.3、HTTP/1.1 的性能
HTTP 协议是基于 TCP/IP ,并且使用了【请求-应答】的通信模式,所以性能的关键就在此处。
长连接:早期 HTTP/1.0 性能上的很大问题就是,没法起一个请求就要新建一次 TCP 连接,而且是串航请求,做了所谓的 TCP 连接建立和断开,增加了通信的开销。HTTP/1.1 提出了长连接的通信方式,也叫持久连接,好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务端的负载
管道网络传输:即可在同一个 TCP 连接里,客户端可以发起多个请求,只要第一个请求发送出去了,不必等其回来,就可以发第二个请求出去,可以减少整体的响应时间。但是服务器必须按照接受请求的顺序发送对这些管道化请求的响应。所以 HTTP /1.1 管道解决了请求的队头阻塞,但是没有解决响应的队头阻塞
队头阻塞:当顺序发送的请求序列中的一个请求因为某种原因被阻塞时,在后面排队的所有请求也一同被阻塞了,会招致客户端一直请求不到数据,这就是【队头阻塞】
五、HTTP 与 HTTPS
5.1、HTTP 与 HTTPS 的区别
- HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输
- HTTP 连接建立相对简单,TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需要进行 SSL/TLS 的握手过程,才可进入加密报文传输
- 两者的默认端口不一致,HTTP 默认端口是 80 ,HTTPS的默认端口是 443
- HTTPS 协议需要向 CA (证书权威机构)申请数字证书,来保证服务器的身份是可信的
5.2、HTTPS 解决了 HTTP 的哪些问题呢?
- 窃听风险,比如通信链路上可以获取通信内容
- 篡改风险,比如强制植入垃圾广告,视觉污染
- 冒充风险,比如冒充淘宝,钱容易没
如何解决的呢?
- 信息加密:交互信息无法被篡改
- 校验机制:无法篡改通信内容,篡改了就无法显示
- 身份证书:证明某个网站是正版的
具体的实现方法:
- 混合加密实现了信息的机密性,解决了窃听的风险
- 摘要算法的方式来实现完整性,能够为数据生成独一无二的【指纹】,指纹用于校验数据的完整性,解决了篡改的风险
- 将服务器公钥放入到数字证书中,解决了冒充的风险
混合加密:
HTTPS 采用的是对称加密和非对称加密结合的【混合加密】方式:
在通信建立前采用非对称加密的方式交换【会话密钥】,后续就不再使用非对称加密
在通信过程中全部使用对称加密的【会话密钥】的方式加密明文数据
采用混合加密的方式的原因:
- 对称加密只使用一个密钥,运算速度快,密钥无法保密,无法做到安全的密钥交换
- 非对称加密使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但是速度慢
摘要算法 + 数字签名:
在计算机里使用摘要算法(哈希函数)来计算出内容的哈希值,也就是内容的【指纹】,这个哈希值是唯一的,且无法通过哈希值推导出内容。
通过哈希算法可以确保内容不被篡改,但是并不能保证【内容 + 哈希值】不会被中间人替换,因为这里缺少对客户端收到的信息是否来源于服务端的证明。
为了避免这种情况,计算机会采用非对称加密算法来解决:
- 一个是公钥,这个可以公开给所有人
- 一个是私钥,这个必须是本人管理,不可泄漏
这两个密钥可以双向加解密的,比如可以用公钥加密内容,私钥解密,也可以用私钥加密内容,公钥加密内容。
流程不同意味着目的也不同:
- 公钥加密,私钥解密:这个目的是为了保证内容传输的安全,因为被公钥加密的内容,其他人是无法解密的,只有持有私钥的人,才能解密出内容
- 私钥加密,公钥解密:这个目的是为了保证消息不会被冒充,因为私钥是不可被泄露的,如果公钥能正常解密出私钥加密的内容,就能证明这个消息是源于持有私钥身份的人发送的
一般我们不会用非对称加密来加密实际的传输内容,因为非对称加密的计算比较耗费性能的。
所以非对称加密的用途主要是在于通过【私钥加密,公钥解密】的方式,来确认消息的身份,我们常说的数字签名算法就是这种方式,不过私钥加密的内容不是内容本身,而是对内容的哈希值加密,私钥由服务器保管,然后服务器向客户端颁发对应的公钥。如果客户端收到的信息,能被公钥解密,就说明该信息是由服务器发送的。
数字证书
前面我们知道:可以通过哈希算法来保证消息的完整性,可以通过数字签名来保证消息的来源可靠性。但是还远远不够,缺少了身份验证的环节,万一公钥是伪造的呢?
举个🌰:
拿请假的例子,虽然你爸爸持有私钥,老师通过是否能用公钥解密来确认这个请假条是不是来源你父亲的。
但是我们还可以自己伪造出一对公私钥啊!
你找了个夜晚,偷偷把老师桌面上和你爸爸配对的公钥,换成了你的公钥,那么下次你在请假的时候,你继续模仿你爸爸的字迹写了个请假条,然后用你的私钥做个了「数字签名」。
但是老师并不知道自己的公钥被你替换过了,所以他还是按照往常一样用公钥解密,由于这个公钥和你的私钥是配对的,老师当然能用这个被替换的公钥解密出来,并且确认了内容的完整性,于是老师就会以为是你父亲写的请假条,又允许你请假了。
好家伙,为了一个请假,真的是斗智斗勇。
后面你的老师和父亲发现了你伪造公私钥的事情后,决定重新商量一个对策来应对你这个臭家伙。
正所谓魔高一丈,道高一尺。
既然伪造公私钥那么随意,所以你爸把他的公钥注册到警察局,警察局用他们自己的私钥对你父亲的公钥做了个数字签名,然后把你爸爸的「个人信息 + 公钥 + 数字签名」打包成一个数字证书,也就是说这个数字证书包含你爸爸的公钥。
这样,你爸爸如果因为家里确实有事要向老师帮你请假的时候,不仅会用自己的私钥对内容进行签名,还会把数字证书给到老师。
老师拿到了数字证书后,首先会去警察局验证这个数字证书是否合法,因为数字证书里有警察局的数字签名,警察局要验证证书合法性的时候,用自己的公钥解密,如果能解密成功,就说明这个数字证书是在警察局注册过的,就认为该数字证书是合法的,然后就会把数字证书里头的公钥(你爸爸的)给到老师。
由于通过警察局验证了数字证书是合法的,那么就能证明这个公钥就是你父亲的,于是老师就可以安心的用这个公钥解密出请假条,如果能解密出,就证明是你爸爸写的请假条。
正是通过了一个权威的机构来证明你爸爸的身份,所以你的伪造公私钥这个小伎俩就没用了。
在计算机里,这个权威的机构就是 CA(数字证书认证机构),将服务器公钥放在数字证书中,只要证书是可信的,公钥就是可信的。
5.3、HTTPS 是如何建立连接的?
SSL/TLS 协议基本流程:
- 客户端向服务器索要并验证服务器的公钥
- 双方协商生产【会话密钥】
- 双方采用【会话密钥】进行加密通信
........................
5.4、HTTPS 的应用数据是如何保证完整性的?
TLS 在实现上分为握手协议和记录协议两层:
- TLS 握手协议就是我们前面所说的 TLS 四次握手的过程,负责协商加密算法和生成对称密钥,后续用此密钥来保护应用程序数据(即HTTP 数据)
- TLS 记录协议负责保护应用程序数据并验证其完整性和来源,所以对 HTTP 数据加密是使用记录协议
具体过程如下:
- 首先,消息被分割成多个较短的片段,然后分别对每个片段进行压缩
- 接下来,经过压缩的片段会被加上消息认证码(MAC 值,这个就是通过哈希算法生成的),这是为了保证完整性,并进行数据的认证。通过附加消息认证码的 MAC 值,可以识别出篡改。与此同时,为了防止重放攻击,在计算消息认证码时,还加上了片段的编码
- 再接下来,经过压缩的片段再加上消息认证码会一起通过对称密码进行加密
- 最后,上述经过加密的数据再加上由数据类型、版本号、压缩后的长度组成的报头就是最终的报文数
记录协议完成后,最终的报文数据将传递到传输控制协议(TCP)层进行传输
5.5、HTTPS 就一定安全可靠吗?
如果有假基站起了转发全部信息的作用,这样是不是假基站就获取到全部信息了,从而造成数据泄露?
这个场景就是:客户端通过浏览器向服务端发起 HTTPS 请求时,被【假基站】转发到了一个【中间人服务器】,于是客户端是和【中间人服务器】完成了 TLS 握手,然后这个【中间人服务器】再与真正的服务器完成 TLS 握手
具体过程如下:
- 客户端向服务端发起 HTTPS 建立连接请求时,然后被【假基站】转发到了一个【中间人服务器】,接着中间人向服务端发起 HTTPS 建立连接请求,此时客户端与中间人进行 TLS 握手,中间人与服务端进行 TLS 握手;
- 在客户端与中间人进行 TLS 握手的过程中,中间人会发送自己的公钥证书给客户端,客户端验证证书真伪,然后从证书中拿到公钥并生成一个随机数,用公钥加密随机数发送给中间人,中间人使用私钥进行解密,得到随机数,此时双方都有随机数,然后通过算法生成对称加密密钥(A),后续客户端与中间人通信就用这个对称加密密钥来加密数据了。
- 在中间人与服务端进行 TLS 握手过程中,服务端会发送从 CA 机构签发的公钥证书给中间人,从证书中拿到公钥,生成一个随机数,用公钥加密随机数发送给服务端,服务端使用私钥解密,得到随机数,此时双方都有随机数,然后通过算法生成对称加密密钥(B),后续中间人与服务端通信就用这个对称加密密钥来加密数据了
- 后续的通信过程中,中间人用对称加密密钥(A)解密客户端的 HTTPS 请求的数据,然后用对称加密密钥(B)加密 HTTPS 请求后,转发给服务端,接着服务端发送 HTTPS 响应数据给中间人,中间人用对称加密密钥(B)解密 HTTPS 响应数据,然后再用对称加密密钥(A)加密后,转发给客户端。
从客户端的角度看,其实并不知道网络中存在中间人服务器这个角色。那么中间人就可以解开浏览器发起的 HTTPS 请求里的数据,也可以解开服务端响应给浏览器的 HTTPS 响应数据。相当于中间人能够偷看浏览器与服务端之间的 HTTPS 请求与响应数据
但是这是有前提的,前提是用户必须点击接受了中间人服务器的证书。
中间人服务器与客户端在 TLS 握手的过程中实际上发送了自己为造册证书给浏览器,而这个伪造的证书是能被浏览器(客户端)识别出是非法的,于是会提醒用户该证书存在问题。如果用户执意点击【继续浏览此网站】,相当于接受了中间人伪造的证书,那么后续整个 HTTPS 通信都能被中间人监听了。另外,如果你的电脑中毒了,被恶意导入了中间人的根证书,那么在验证中间人的证书的时候,由于你的操作系统信任了中间人的根证书,那么等同于中间人的证书是合法的,这种情况下,浏览器是不会弹出任何存在问题的风险提示的。
5.6、为什么抓包工具能够截取 HTTPS 数据?
很多抓包工具之所以可以明文看到 HTTPS 数据,工作原理与中间人一致
对于 HTTPS 连接来说,中间人要满足以下两点,才能实现真正的明文代理:
- 中间人,作为客户端与真实服务端建立连接的这一步不会有问题,因为服务端不会校验客户端的身份
- 中间人作为服务端与真实客户端建立连接,这里会有客户端信任服务端的问题,也就是服务端必须有对应域名的私钥
中间人要拿到私钥只能通过如下方式:
- 去网站服务端拿到私钥
- 去 CA 处拿域名签发私钥
- 自己签发证书,切要被浏览器信任
很显然只能通过第三种取得中间人的身份,使用抓包工具进行 HTTPS 抓包的时候,需要在客户端安装 Fiddler 的根证书,这里实际上起到认证中心(CA)的作用。抓包工具能够抓包的关键是客户端会往系统受信任的根证书列表中导入抓包工具生成的证书,而这个证书会被浏览器信任,也就是抓包工具自己创建了一个认证中心CA,客户端拿着中间人签发的证书去中间人自己的 CA 去认证,当然认为这个证书是有效的。
如何避免被中间人抓取信息呢?
首先我们要保证自己电脑的安全,不要被病毒侵入,而且也不要点击任何证书非法的网站,这样 HTTPS 数据就不会被中间人截取到了,当然还可以通过 HTTPS 双向认证的方式来避免这种问题,一般我们的 HTTPS 都是单向认证,客户端只会验证了服务端的身份,但是服务端不会验证客户端的身份,如果服务端检测到了客户端是不可信任的,服务端就拒绝通信,客户端如果发现服务端也是不可信任的,也终止通信。
六、HTTP/1.1、HTTP/2、HTTP/3 的演变
6.1、HTTP/1.1 相比 HTTP/1.0 提高了什么性能?
HTTP/1.1 相比 HTTP/1.0 性能上的改进:
- 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销
- 支持管道网络传输,只要第一个请求发送出去了,不必等其回来,就可以发送第二个请求出去,可以减少整体的响应时间
但 HTTP/1.1 还是有性能瓶颈的:
- 请求 / 响应头部未经压缩就发送,首部信息越多延迟越大。只能压缩 Body 的部分
- 发送冗长的首部,每次互相发送相同的首部造成的浪费越多
- 服务器是按请求的顺序相应的,如果服务器响应慢,会招致客户端一直请求不到数据,也就是队头阻塞
- 没有请求优先级控制
- 请求只能从客户端开始,服务器只能被动响应
6.2、HTTP/2 的优化
HTTP/2 是基于 HTTPS 的,所以 HTTP/2 的安全性也是由保证的
HTTP/2 相比 HTTP/1.1 性能上的优化:
头部压缩:如果你同时发出多个请求,它们的头部是一样的或者类似的,那么协议会帮你消除重复的部分这就是【HPACK】算法:在客户端和服务器同时维护一张头信息表,所有字段都会存入这个表,生成一个索引号,以后就不发送同样的字段了,只发送索引号,就能提高速度了
二进制格式:不再像 HTTP/1.1 里的纯文本形式的报文,而是全面采用了二进制格式,头信息和数据体都是二进制,并且统称为帧:头信息帧和数据帧。虽然对人不太友好,但是对计算机非常好,收到报文之后,无需再将明文的报文转化成二进制,而是直接解析二进制报文,这增加了数据的传输效率。
并发传输:我们都知道 HTTP/1.1 是基于【请求-响应】模型的,同一个连接中,HTTP 完成一个事务,才能处理下一个事务,也就是说发出请求等待响应的过程中,是没办法做其他事情的,如果响应迟迟下不来,那么后续的请求是无法被发送的,也就造成了队头堵塞的问题。而 2.0 引入了 Stream 的概念。多个 Stream 复用在一条 TCP 连接上,Stream 中可以包含多个 message ,一个message 中包含多个 Frame ,Frame 是 2.0 的最小单位,以二进制压缩格式存放HTTP/1 的内容。针对不同的 HTTP 请求用独一无二的 Stream ID 来区分,接收端可以通过 Stream ID 有序组装成 HTTP 消息,不同的 Stream 的帧是可以乱序发送的,因此可以并发不同的 Stream ,也就是 HTTP/2 可以并行交错发送请求和响应,服务端收到后会根据相同的 Stream ID 有序组装成 HTTP 消息。
服务器主动推送资源:服务器不再是被动地响应,可以主动的向客户端发送消息,客户端和服务端都可以发送建立 Stream ,Stream ID 号是有区别的,客户端建立的 Stream 必须是奇数号,而服务器建立的 Stream 必须是偶数号。比如,客户端通过 请求从服务器获取到了 html 文件,而 html文件还需要 css 文件渲染,这时客户端还要再次发起获取 css 文件的请求,需要消息往返两次,如果是HTTP/2 服务器可以直接主动推送 css 文件,减少了消息传递的次数。
6.3、HTTP/2 的缺陷
HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当【前一个字节数据】没有到达时,后收到的字节数据只能存放在内核缓冲区中,只有等到这一个字节数据到达时,HTTP/2 应用层才能从内核拿到数据,这就是 HTTP/2 队头阻塞的问题。所以,一旦出现了丢包现象,就会触发 TCP 的重传机制,这样在一个 TCP 连接中的所有 hTTP 请求都必须等待这个丢了的包被重传过来。
6.4、HTTP/3 做了哪些优化?
HTTP/1.1 和 HTTP/2 都有队头阻塞的问题:
HTTP/1.1 中的管道虽然解决了请求的队头阻塞,但是没有解决响应的队头阻塞,因为服务端需要按顺序响应收到的请求,如果服务端处理某个请求消耗的时间比较长,那么只能等待响应完这个请求后,才能处理下一个请求,这属于 HTTP 层队头阻塞。
HTTP/2 虽然通过多个请求复用一个 TCP 连接解决了 HTTP 的队头阻塞,但是一旦发生丢包,就会阻塞住所有的 HTTP 请求,这属于 TCP 层队头阻塞。
HTTP/3 是如何解决的呢?
HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP 协议!
UDP 传输协议是不管发送顺序、丢包的,所以不会出现像 HTTP/2 队头阻塞的问题。但基于 UDP 的 QUIC 协议,可以实现类似 TCP 的可靠传输。
QUIC 的 3 特点:
- 无队头阻塞:当某个流发生丢包时,只会阻塞这个流,其他流不会受到影响,因此不存在队头阻塞问题
- 更快的连接建立:虽然需要进行 QUIC 协议的握手,但是这个握手仅需 1 RTT,握手的目的是为了确认双方的【连接ID】,连接迁移就是基于连接 ID 来实现的。在 3 中,QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 的记录。再加上 QUIC 使用的是 TLS/1.3 ,因此仅需一个 RTT 就可以同时完成建立连接与密钥协商
- 连接迁移:基于 TCP 传输协议的 HTTP 协议,由于是通过四元组(源 IP,源端口,目的IP,目的端口)确定一条 TCP 的连接。那么当移动设备的网络从 4G 切换到 wifi 时。意味着 IP 地址也变化了,那么就需要断开连接,重新建立连接。而 QUIC 协议没有使用四元组来绑定连接,而是通过连接 ID 来标记自己,因此即使移动设备的网络变化之后,导致 IP 地址变化了,只要仍保有上下文信息,就可以无缝复用原连接,消除连接的成本,达到了连接迁移的功能
七、HTTP/1.1 如何优化?
从三个优化思路来优化 HTTP/1.1协议:
- 尽量避免发送 HTTP 请求
- 在需要发送 HTTP 请求时,考虑如何减少请求次数
- 减少服务器的 HTTP 响应的数据大小
7.1、避免发送 HTTP 请求
对于一些具有重复性的 HTTP 请求,比如每次请求的数据都是相同的,我们可以把这对【请求-响应】的数据都缓存在本地,那么下次就直接读取本地的数据,不必在通过网络获取服务器的响应。
7.2、如何减少 HTTP 请求次数?
从这几个方面入手:
- 减少重定向请求次数
- 和并请求
- 延迟发送请求
什么是重定向:服务器上的一个资源可能由于迁移等原因从 url1 移至 url2 后,客户端不知情,还是继续请求 url1,这是服务器不能暴力返回错误,而是通过 302 响应码和 Location 头部,告诉客户端该资源已经迁移至 url2 了,于是客户端需要再发送 url2 请求以获得服务器的资源。
7.2.1、减少重定向次数:
如果重定向请求越多,那么客户端就要多次发起 HTTP 请求,每一次的 HTTP 请求都得经过网络,这无疑会降低网络性能。
另外,服务端这一方往往不只有一台服务器,比如源服务器上一级是代理服务器,然后代理服务器与客户端通信,这时客户端重定向就会导致客户端与代理服务器之间需要 2 次消息传递,如果将重定向的工作交由代理服务器完成就能减少 HTTP 请求的次数啦:
7.2.2、和并请求
把多个访问小文件的请求合并成一个大的请求,虽然传输的总资源还是一样的,但是减少请求,也就意味着减少了重复发送的 HTTP 头部。
例如可以将多个小图片的请求合并成一个大图片来减少 HTTP 请求的次数,从而减少网络的开销。合并请求的方式就是合并资源,以一个大资源的请求替换多个小资源的请求。
但是这样会带来新的问题,当大资源中的某一个小资源发生变化后,客户端必须重新下载整个完整的大资源文件,这显然会带来额外的网络消耗。
7.2.3、延迟发送请求
一般 HTML 里会含有很多 HTTP 的 URL ,当前不需要的资源,我们没必要获取过来,于是可以通过按需获取的方式,来减少第一时间的 HTTP 请求次数。
请求网页的时候,没必要把全部的资源都获取到,而是只获取当前用户所看到的页面资源,当用户向下滑动的时候,再向服务器获取接下来的资源,这样就达到了延迟发送请求的效果。
7.3、如何减少 HTTP 响应的数据大小
对于 HTTP 的请求和响应,通常 HTTP 的响应数据大小会比较大,也就是服务器返回的资源会比较大。于是我们可以考虑对响应的资源进行压缩,这样就可以减少响应的数据大小,从而提高网络传输的效率。
八、HTTPS RSA 握手解析
传统的 TLS 握手基本都是使用 RSA 算法来实现密钥交换的,在将 TLS 证书部署服务端时,证书文件其实就是服务端的公钥,会在 TLS 握手阶段传递给客户端,而服务端的私钥则一直留在服务端,一定要确保私钥不能被窃取。
在 RSA 密钥协商算法中,客户端会生成随机密钥,并使用服务端的公钥加密后再传给服务端。根据非对称算法,公钥加密的消息仅能通过私钥解密,这样服务端解密后,双方就获取了相同的密钥,再用它加密应用信息。
RSA 握手握手过程:
TLS 第一次握手:
客户端首先会发一个【Client Hello】消息,这是跟服务器【打招呼】,消息里面有客户端使用的 TLS 版本号,支持的密码套件列表,以及生成的【随机数(Client Random)】,这个随机数会被服务端保留,它是生成对称加密密码的材料之一。
TLS 第二次握手:
当服务端收到客户端的【Client Hello】消息后,会确认 TLS 版本号是否支持,和从密码套件列表中选择一个密码套件,以及生成随机数(Server Random)。
接着返回【Server Hello】消息,消息里面有服务器确认的 TLS 版本号,也给出了随机数(Server Hello),然后从客户端的密码套件列表选择了一个合适的密码套件。
密码套件形式基本是【密码交换算法 + 签名算法 + 对称加密算法 + 摘要算法】。
就前面这两个客户端和服务端相互【打招呼】的过程,客户端和服务端就已经确认了 TLS 版本和使用的密码套件,而且你可能发现客户端和服务端都会各自生成一个随机数,并且还会把随机数传递给对方。这个随机数的作用就是为后续作为生成【会话密钥】的条件,所谓的会话密钥就是数据传输时,所使用的对称加密密钥。
然后服务端为了证明自己的身份,就会发送【Server Certificate】给客户端,这个消息里有数字证书,随后,服务端发了【Server Hello Done】消息,目的是告诉客户端,我已经把该给你的东西都给你了。
TLS 第三次握手:
客户端验证完第二次握手发送来的证书之后,认为可信则继续。
接着,客户端会生成一个新的随机数(pre-master),用服务器的 RSA 公钥加密该随机数,通过【Client Key Exchange】消息传递给服务端。
服务端收到后,用 RSA 私钥解密,得到客户端发来的随机数(pre-master)。
至此,客户端和服务端双方都共享了三个随机数,分别是 Client Random,Server Random,pre-master。于是,双方根据已经得到得三个随机数,生成会话密钥(Master Secret),它是对称密钥,用于后续的 HTTP 请求/响应的数据加解密.然后客户端再发送一个【Encrypted Handshake Message(Finshd)】消息,把之前所有发送的数据做个摘要,再用会话密钥(master secret)加密一下,让服务器做个验证,验证加密通信是否可用和【之前握手信息是否有被中途篡改过】,可以发现,【Change Cipher Spec】之前传输的 TLS 握手数据都是明文,之后都是对称加密密钥的密文。
TLS 第四次握手
服务器也是同样的操作,发【Change Cipher Spec】和【Encrypted Handshake Message】消息,如果双方都验证加密和解密没问题,那么握手正式完成。最后,就用【会话加密】加解密 HTTP 请求和响应了
RSA 算法的缺陷
使用 RSA 密钥协商算法的最大问题是不支持前置保密,因为客户端传递随机数给服务端时使用的是公钥加密的,服务端收到后,会用私钥解密得到随机数。所以一旦服务端的私钥泄露了,过去被第三方捕获的所有 TLS 通讯密文都会被破解
九、优化 HTTPS
硬件优化:HTTPS 协议是计算密集型,而不是 IO 密集型,所以可以升级性能更好的 CPU 或者选择可以支持 AES-NI 特性的 CPU,因为这款 CPU 在指令级别优化了 AES 算法,加速了加解密时间。
密钥交换算法优化:使用 RSA 密钥交换算法的 TLS 握手过程不仅慢,而且安全性也不高,如果可以尽量使用 ECDHE 密钥交换算法替换 RSA 算法。可以将 TLS 握手的消息往返由 2 RTT 减少到 1 RTT,而且安全性高,具备前向安全性
TLS 升级:直接把 TLS 的版本升级,大幅度减少握手的步骤,完成 TLS 握手只要 1 RTT,而且安全性更高
证书传递优化:减少证书的大小,可以节省带宽,减少客户的运算量。对于服务器的证书应该选择椭圆曲线证书(ECDSA)证书,而不是 RSA 证书,因为在相同安全强度下,ECC密钥长度比 RSA 要短得多
会话复用:会话复用分为两种一种是 Session ID,另外一种是 Session Ticket。
Session ID:客户端与服务端首次 TLS 握手连接后,双方会在内存缓存会话密钥用唯一的 Session ID 来标识,再次连接时,Hello 消息里就会带上 Session ID,服务器收到后就会在内存中找,如果找到就直接使用该会话密钥回复会话状态,跳过其余的过程,只用一个消息往返就可以建立安全通信。
缺点:
- 服务器必须保持每一个客户端的会话密钥,随着客户端的增多,服务器的内存压力也会越来越大
- 现在网站服务一般是由多台服务器通过负载均衡提供服务的,客户端再次连接不一定会命中上次访问过的服务器,于是还是要走完整的 TLS 握手过程
Session Ticket:服务器不再缓存每个客户端的会话密钥,而是把缓存的工作交给了客户端,类似于 HTTP 中的 Cookie,但是要注意的是需要对会话密钥设定一个合理的过期时间避免重放攻击。
重放攻击:
假设 A 想向 B 证明自己的身份。B 要求 A 的密码作为身份证明,A 应尽全力提供。与此同时 E 窃听了对话并保留了密码。
交换结束后,E 冒充 A 连接到 B ,当被要求提供身份证明时,E 发送从 B 接受的最后一个会话中读取的 A 的密码从而授予 E 访问权限。
重放攻击的危险之处在于,如果中间人截获了某个客户端的 Session ID 或 Session Ticket 以及 POST 报文,而一般的 POST 请求会改变数据库的数据,中间人就可以利用此截获的报文,不断地向服务器发送该报文,这样就会导致数据库的数据被中间人改变了,而客户是不知情的。