文章为小林coding的学习笔记,包含自己学习中的思考和认为重点记忆的内容
HTTP常见面试题
HTTP基本概念
HTTP 是⼀个在计算机世界⾥专⻔在「两点」之间「传输」⽂字、图⽚、⾳频、视频等「超⽂本」数据的「约定和规范」。
1.1.1. 名称
HTTP 是超⽂本传输协议,也就是HyperText Transfer Protoco
1.1.2. 允许中间有中转或接⼒。
数据虽然是在 A 和 B 之间传输,但允许中间有中转或接⼒。
就好像第⼀排的同学想传递纸条给最后⼀排的同学,那么传递的过程中就需要经过好多个同学(中间
⼈),这样的传输⽅式就从「A < --- > B」,变成了「A <-> N <-> M <-> B」。
⽽在 HTTP ⾥,需要中间⼈遵从 HTTP 协议,只要不打扰基本的数据传输,就可以添加任意额外的东⻄。
针对传输,我们可以进⼀步理解了 HTTP。
HTTP 是⼀个在计算机世界⾥专⻔⽤来在两点之间传输数据的约定和规范。
1.1.3. 错误码
1xx 类状态码属于提示信息,是协议处理中的⼀种中间状态,实际⽤到的⽐较少。
2xx 类状态码表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态。
- 「200 OK」是最常⻅的成功状态码,表示⼀切正常。如果是⾮ HEAD 请求,服务器返回的响应头都会有 body 数据。
- 「204 No Content」也是常⻅的成功状态码,与 200 OK 基本相同,但响应头没有 body 数据。
- 「206 Partial Content」是应⽤于 HTTP 分块下载或断点续传,表示响应返回的 body 数据并不是资源的全部,⽽是其中的⼀部分,也是服务器处理成功的状态。
3xx 类状态码表示客户端请求的资源发⽣了变动,需要客户端⽤新的 URL 重新发送请求获取资源,也就是重定向。
- 「301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改⽤新的 URL 再次访问。
- 「302 Found」表示临时重定向,说明请求的资源还在,但暂时需要⽤另⼀个 URL 来访问。
- 301 和 302 都会在响应头⾥使⽤字段 Location ,指明后续要跳转的 URL,浏览器会⾃动重定向新的URL。
- 「304 Not Modified」不具有跳转的含义,表示资源未修改,重定向已存在的缓冲⽂件,也称缓存重定向,也就是告诉客户端可以继续使⽤缓存资源,⽤于缓存控制。
4xx 类状态码表示客户端发送的报⽂有误,服务器⽆法处理,也就是错误码的含义。
- 「400 Bad Request」表示客户端请求的报⽂有错误,但只是个笼统的错误。
- 「403 Forbidden」表示服务器禁⽌访问资源,并不是客户端的请求出错。
- 「404 Not Found」表示请求的资源在服务器上不存在或未找到,所以⽆法提供给客户端。
5xx 类状态码表示客户端请求报⽂正确,但是服务器处理时内部发⽣了错误,属于服务器端的错误码。
- 「500 Internal Server Error」与 400 类型,是个笼统通⽤的错误码,服务器发⽣了什么错误,我们并不知道。
- 「501 Not Implemented」表示客户端请求的功能还不⽀持,类似“即将开业,敬请期待”的意思。
- 「502 Bad Gateway」通常是服务器作为⽹关或代理时返回的错误码,表示服务器⾃身⼯作正常,访问后端服务器发⽣了错误。
- 「503 Service Unavailable」表示服务器当前很忙,暂时⽆法响应客户端,类似“⽹络服务正忙,请稍后重试”的意思。
1.1.4. 什么是RFC
RFC(Request For Comments)-意即“请求注解”,包含了关于Internet的几乎所有重要的文字资料。如果你想成为网络方面的专家,那么RFC无疑是最重要也是 最经常需要用到的资料之一,所以RFC享有网络知识圣经之美誉。通常,当某家机构或团体开发出了一套标准或提出对某种标准的设想,想要征询外界的意见时, 就会在Internet上发放一份RFC,对这一问题感兴趣的人可以阅读该RFC并提出自己的意见;绝大部分网络标准的指定都是以RFC的形式开始,经过 大量的论证和修改过程,由主要的标准化组织所指定的,但在RFC中所收录的文件并不都是正在使用或为大家所公认的,也有很大一部分只在某个局部领域被使用 或并没有被采用,一份RFC具体处于什么状态都在文件中作了明确的标识。
1.1.5. HTTP长连接
在 HTTP 1.0 中默认是关闭的,如果浏览器要开启 Keep-Alive,它必须在请求的包头中添加
HTTP/1.1 提出了⻓连接的通信⽅式,也叫持久连接。这种⽅式的好处在于减少了 TCP 连接的重复建⽴和断开所造成的额外开销,减轻了服务器端的负载
GET与POST
从RFC规范定义来看:
- GET方法是安全且幂等的,GET ⽅法就是安全且幂等的,因为它是**「只读」**操作,⽆论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。所以, 可以对 GET 请求的数据做缓存,这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如nginx)。
- POST 因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。所以, 浏览器⼀般不会缓存 POST 请求,也不能把 POST请求保存为书签。
但是实际过程中,开发者不⼀定会按照 RFC 规范定义的语义来实现 GET 和 POST ⽅法。
⽐如:
- 可以⽤ GET ⽅法实现新增或删除数据的请求,这样实现的 GET ⽅法⾃然就不是安全和幂等。
- 可以⽤ POST ⽅法实现查询数据的请求,这样实现的 POST ⽅法⾃然就是安全和幂等。
如果「安全」放⼊概念是指信息是否会被泄漏的话,虽然 POST ⽤ body 传输数据,⽽ GET ⽤ URL 传输,这样数据会在浏览器地址拦容易看到,但是并不能说 GET 不如 POST 安全的。
因为 HTTP 传输的内容都是明⽂的,虽然在浏览器地址拦看不到 POST 提交的 body 数据,但是只要抓个包就都能看到了。
所以,要避免传输过程中数据被窃取,就要使⽤ HTTPS 协议,这样所有 HTTP 的数据都会被加密传输。
GET 请求可以带 body 吗?
RFC 规范并没有规定 GET 请求不能带 body 的。理论上,任何请求都可以带 body 的。只是因为 RFC 规范定义的 GET 请求是获取资源,所以根据这个语义不需要⽤到 body。另外, URL 中的查询参数也不是 GET 所独有的, POST 请求的 URL 中也可以有参数的。
HTTP缓存技术
强制缓存
- Cache-Control , 是⼀个相对时间;
- Expires ,是⼀个绝对时间;
协商缓存
当我们在浏览器使⽤开发者⼯具的时候,你可能会看到过某些请求的响应码是 304 ,这个是告诉浏览器可以使⽤本地缓存的资源,通常这种通过服务端告知客户端是否可以使⽤缓存的⽅式被称为协商缓存。
协商缓存这两个字段都需要配合强制缓存中 Cache-Control 字段来使⽤,只有在未能命中强制缓存
的时候,才能发起带有协商缓存字段的请求。
HTTP/1.1、HTTP/2、HTTP/3演变
HTTP/1.1
改进:
- 使⽤⻓连接的⽅式改善了 HTTP/1.0 短连接造成的性能开销。
- ⽀持管道(pipeline)⽹络传输,只要第⼀个请求发出去了,不必等其回来,就可以发第⼆个请求出去,可以减少整体的响应时间。
不足:
- 请求 / 响应头部(Header)未经压缩就发送,⾸部信息越多延迟越⼤。只能压缩 Body 的部分;
- 发送冗⻓的⾸部。每次互相发送相同的⾸部造成的浪费较多;
- 服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端⼀直请求不到数据,也就是队头阻塞;
- 没有请求优先级控制;
- 请求只能从客户端开始,服务器只能被动响应。
HTTP/2
改进:
- 头部压缩
- ⼆进制格式
- 并发传输
- 服务器主动推送资源
不足:
HTTP/2 还是存在“队头阻塞”的问题,只不过问题不是在 HTTP 这⼀层⾯,⽽是在 TCP 这⼀层。
HTTP/2 是基于 TCP 协议来传输数据的, TCP 是字节流协议, TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区⾥的数据返回给 HTTP 应⽤,那么当「前 1 个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区⾥,只有等到这 1 个字节数据到达时, HTTP/2 应⽤层才能从内核中拿到数据,这就是 HTTP/2 队头阻塞问题。
TTP/2 虽然通过多个请求复⽤⼀个 TCP 连接解决了 HTTP 的队头阻塞 ,但是⼀旦发⽣丢包,就会阻塞住所有的 HTTP 请求,这属于 TCP 层队头阻塞。
HTTP/3
改进: HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP!
UDP 发送是不管顺序,也不管丢包的,所以不会出现像 HTTP/2 队头阻塞的问题。⼤家都知道 UDP 是不可靠传输的,但基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输。
QUIC 有以下 3 个特点:
- ⽆队头阻塞
- 更快的连接建⽴
- 连接迁移
原理:QUIC 是⼀个在 UDP 之上的伪 TCP + TLS + HTTP/2 的多路复⽤的协议。
不足:QUIC 是新协议,对于很多⽹络设备,根本不知道什么是 QUIC,只会当做 UDP,这样会出现新的问题,因为有的⽹络设备是会丢掉 UDP 包的,⽽ QUIC 是基于 UDP 实现的,那么如果⽹络设备⽆法识别这个是QUIC 包,那么就会当作 UDP包,然后被丢弃。
HTTP/1.1 如何优化?
1.1. 缓存
客户端会把第⼀次请求以及响应的数据保存在本地磁盘上,其中将请求的 URL 作为 key,⽽响应作为value,两者形成映射关系。这样当后续发起相同的请求时,就可以先在本地磁盘上通过 key 查到对应的 value,也就是响应,如果找到了,就直接从本地读取该响应。毋庸置疑,读取本地磁盘的速度肯定⽐⽹络请求快得多
1.2. 如何减少HTTP请求次数
1.2.1. 减少重定向请求次数;
- 重定向的⼯作交由代理服务器完成,就能减少 HTTP 请求次数了
1.2.2. 合并请求
- 减少了重复发送的 HTTP 头部
所以⼀般浏览器会同时发起 5-6 个请求,每⼀个请求都是不同的 TCP 连接,那么如果合并了请求,也就会减少 TCP 连接的数量,因⽽省去了 TCP 握⼿和慢启动过程耗费的时间。
具体合并的方式:
- 对于⼩图⽚,我们可以考虑使⽤CSS Image Sprites 技术把它们合成⼀个⼤图⽚,然后用CSS再切割为小图片
- 服务端使⽤ webpack 等打包⼯具将 js、 css 等资源合并打包成⼤⽂件,也是能达到类似的效果。
- 还可以将图⽚的⼆进制数据⽤ base64 编码后,以 URL 的形式嵌⼊到 HTML ⽂件,跟随 HTML⽂件⼀并发送。
弊端:
- 当⼤资源中的某⼀个⼩资源发⽣变化后,客户端必须重新下载整个完整的⼤资源⽂件
1.2.3. 延迟发送请求
按需获取:⼀般 HTML ⾥会含有很多 HTTP 的 URL,当前不需要的资源,我们没必要也获取过来。
请求⽹⻚的时候,没必要把全部资源都获取到,⽽是只获取当前⽤户所看到的⻚⾯资源,当⽤户向下滑动⻚⾯的时候,再向服务器获取接下来的资源,这样就达到了延迟发送请求的效果。
1.3. 如何减少HTTP响应的数据大小
压缩的方式一般分为 2 种,分别是:
- 无损压缩:通常针对文本文件
- 删除换行符和空格
- 建立统计模型,用霍夫曼编码(常出现的频率高),gzip是常见的无损压缩。
- 有损压缩:通常针对媒体文件
- 牺牲一些质量,压缩媒体文件
- 音视频的压缩,音视频主要是动态的,每个帧都有时序的关系,通常时间连续的帧之间的变化是很小的。就可以考虑使用增量数据来表达后续的帧。
1.4. 提升并发性
将同一个页面的资源分散到不同域名,提升并发连接上限,因为浏览器通常对同一域名的 HTTP 连接最大只能是 6 个;
1.5. 总结
这次主要从 4个方面介绍了优化 HTTP/1.1 协议的思路。
第一个思路是,通过缓存技术来避免发送 HTTP 请求。客户端收到第一个请求的响应后,可以将其缓存在本地磁盘,下次请求的时候,如果缓存没过期,就直接读取本地缓存的响应数据。如果缓存过期,客户端发送请求的时候带上响应数据的摘要,服务器比对后发现资源没有变化,就发出不带包体的 304 响应,告诉客户端缓存的响应仍然有效。
第二个思路是,减少 HTTP 请求的次数,有以下的方法:
- 将原本由客户端处理的重定向请求,交给代理服务器处理,这样可以减少重定向请求的次数;
- 将多个小资源合并成一个大资源再传输,能够减少 HTTP 请求次数以及 头部的重复传输,再来减少 TCP 连接数量,进而省去 TCP 握手和慢启动的网络消耗;
- 按需访问资源,只访问当前用户看得到/用得到的资源,当客户往下滑动,再访问接下来的资源,以此达到延迟请求,也就减少了同一时间的 HTTP 请求次数。
第三思路是,通过压缩响应资源,降低传输资源的大小,从而提高传输效率,所以应当选择更优秀的压缩算法。
提升并发性:将同一个页面的资源分散到不同域名,提升并发连接上限,因为浏览器通常对同一域名的 HTTP 连接最大只能是 6 个;
不管怎么优化 HTTP/1.1 协议都是有限的,不然也不会出现 HTTP/2 和 HTTP/3 协议,后续我们再来介绍 HTTP/2 和 HTTP/3 协议。
HTTPS SSL/TSL
TLS解决了什么
- 窃听风险,比如通信链路上可以获取通信内容,用户号容易没。
- 篡改风险,比如强制植入垃圾广告,视觉污染,用户眼容易瞎。
- 冒充风险,比如冒充淘宝网站,用户钱容易没。
TLS 协议是如何解决 HTTP 的风险的呢?
- 信息加密:HTTP 交互信息是被加密的,第三方就无法被窃取;
- 校验机制:校验信息传输过程中是否有被第三方篡改过,如果被篡改过,则会有警告提示;
- 身份证书:证明淘宝是真的淘宝网;
针对篡改风险的解决办法:TLS 协议在每个消息上都会使用一个消息认证码(MAC),以验证数据是否在传输过程中被篡改。在接收到数据包时,收件人将计算自己的 MAC,并将其与发送者提供的 MAC 进行比较。如果两者不匹配,那么数据可能已经被篡改,收件人会拒绝该数据包。
RSA算法的缺陷
使用 RSA 密钥协商算法的最大问题是不支持前向保密。
因为客户端传递随机数(用于生成对称加密密钥的条件之一)给服务端时使用的是公钥加密的,服务端收到后,会用私钥解密得到随机数。所以一旦服务端的私钥泄漏了,过去被第三方截获的所有 TLS 通讯密文都会被破解。
HTTPS ECDHE
RSA 是比较传统的密钥交换算法,它不具备前向安全的性质,因此现在很少服务器使用的。而 ECDHE 算法具有前向安全,所以被广泛使用。
DH->DHE->ECDHE
DH
缺陷:
static DH 算法里有一方的私钥是静态的,也就说每次密钥协商的时候有一方的私钥都是一样的,一般是服务器方固定,即 a 不变,客户端的私钥则是随机生成的
于是,DH 交换密钥时就只有客户端的公钥是变化,而服务端公钥是不变的,那么随着时间延长,黑客就会截获海量的密钥协商过程的数据,因为密钥协商的过程有些数据是公开的,黑客就可以依据这些数据暴力破解出服务器的私钥,然后就可以计算出会话密钥了,于是之前截获的加密数据会被破解,所以 static DH 算法不具备前向安全性
DHE
既然固定一方的私钥有被破解的风险,那么干脆就让双方的私钥在每次密钥交换通信时,都是随机生成的、临时的,这个方式也就是 DHE 算法,E 全称是 ephemeral(临时性的)。
所以,即使有个牛逼的黑客破解了某一次通信过程的私钥,其他通信过程的私钥仍然是安全的,因为每个通信过程的私钥都是没有任何关系的,都是独立的,这样就保证了「前向安全」。
总结
RSA 和 ECDHE 握手过程的区别:
- RSA 密钥协商算法「不支持」前向保密,ECDHE 密钥协商算法「支持」前向保密;
- 使用了 RSA 密钥协商算法,TLS 完成四次握手后,才能进行应用数据传输,而对于 ECDHE 算法,客户端可以不用等服务端的最后一次 TLS 握手,就可以提前发出加密的 HTTP 数据,节省了一个消息的往返时间(这个是 RFC 文档规定的,具体原因文档没有说明,所以这点我也不太明白);
- 使用 ECDHE, 在 TLS 第 2 次握手中,会出现服务器端发出的「Server Key Exchange」消息,而 RSA 握手过程没有该消息;
HTTPS如何优化
由裸数据传输的 HTTP 协议转成加密数据传输的 HTTPS 协议,给应用数据套了个「保护伞」,提高安全性的同时也带来了性能消耗。
因为 HTTPS 相比 HTTP 协议多一个 TLS 协议握手过程,目的是为了通过非对称加密握手协商或者交换出对称加密密钥,这个过程最长可以花费掉 2 RTT,接着后续传输的应用数据都得使用对称加密密钥来加密/解密。
分析性能损耗
产生性能消耗的两个环节:
- 第一个环节, TLS 协议握手过程;
- 第二个环节,握手后的对称加密报文传输。
对于第二环节,现在主流的对称加密算法 AES、ChaCha20 性能都是不错的,而且一些 CPU 厂商还针对它们做了硬件级别的优化,因此这个环节的性能消耗可以说非常地小。
而第一个环节,TLS 协议握手过程不仅增加了网络延时(最长可以花费掉 2 RTT),而且握手过程中的一些步骤也会产生性能损耗,比如:
- 对于 ECDHE 密钥协商算法,握手过程中会客户端和服务端都需要临时生成椭圆曲线公私;
- 客户端验证证书时,会访问 CA 获取 CRL 或者 OCSP,目的是验证服务器的证书是否有被销;
- 双方计算 Pre-Master,也就是对称加密密钥;
硬件优化
HTTPS 协议是计算密集型,而不是 I/O 密集型
另外,如果可以,应该选择可以支持 AES-NI 特性的 CPU,因为这种款式的 CPU 能在指令级别优化了 AES 算法,这样便加速了数据的加解密传输过程。
软件优化
- 将 Linux 内核从 2.x 升级到 4.x;
- 将 OpenSSL 从 1.0.1 升级到 1.1.1;
同时也存在一定的风险,也可能会影响正常的线上服务。
协议优化
1.1. 密钥交换算法优化
总之使用 RSA 密钥交换算法的 TLS 握手过程,不仅慢,而且安全性也不高。
因此如果可以,尽量选用 ECDHE 密钥交换算法替换 RSA 算法,因为该算法由于支持「False Start」,它是“抢跑”的意思,客户端可以在 TLS 协议的第 3 次握手后,第 4 次握手前,发送加密的应用数据,以此将 TLS 握手的消息往返由 2 RTT 减少到 1 RTT,而且安全性也高,具备前向安全性。
不同的椭圆曲线性能也不同,根据需求选择更快的曲线。
1.2. TSL升级
把 TLS 1.2 升级成 TLS 1.3,TLS 1.3 大幅度简化了握手的步骤,完成 TLS 握手只要 1 RTT,而且安全性更高。
在 TLS 1.2 的握手中,一般是需要 4 次握手,先要通过 Client Hello (第 1 次握手)和 Server Hello(第 2 次握手) 消息协商出后续使用的加密算法,再互相交换公钥(第 3 和 第 4 次握手),然后计算出最终的会话密钥,下图的左边部分就是 TLS 1.2 的握手过程:
上图的右边部分就是 TLS 1.3 的握手过程,可以发现 TLS 1.3 把 Hello 和公钥交换这两个消息合并成了一个消息,于是这样就减少到只需 1 RTT 就能完成 TLS 握手。
怎么合并的呢?具体的做法是,客户端在 Client Hello 消息里带上了支持的椭圆曲线,以及这些椭圆曲线对应的公钥。
服务端收到后,选定一个椭圆曲线等参数,然后返回消息时,带上服务端这边的公钥。经过这 1 个 RTT,双方手上已经有生成会话密钥的材料了,于是客户端计算出会话密钥,就可以进行应用数据的加密传输了。
而且,TLS1.3 对密码套件进行“减肥”了, 对于密钥交换算法,废除了不支持前向安全性的 RSA 和 DH 算法,只支持 ECDHE 算法。
证书优化
1.1. 证书传输优化
对于服务器的证书应该选择椭圆曲线(ECDSA)证书,而不是 RSA 证书,因为在相同安全强度下, ECC 密钥长度比 RSA 短的多。
1.2. 证书验证优化
1.2.1. CRL
CRL 称为证书吊销列表(Certificate Revocation List),这个列表是由 CA 定期更新,列表内容都是被撤销信任的证书序号,如果服务器的证书在此列表,就认为证书已经失效,不在的话,则认为证书是有效的。
缺陷:
- 由于 CRL 列表是由 CA 维护的,定期更新,如果一个证书刚被吊销后,客户端在更新 CRL 之前还是会信任这个证书,实时性较差;
- 随着吊销证书的增多,列表会越来越大,下载的速度就会越慢,下载完客户端还得遍历这么大的列表,那么就会导致客户端在校验证书这一环节的延时很大,进而拖慢了 HTTPS 连接。
1.2.2. OCSP
因此,现在基本都是使用 OCSP ,名为在线证书状态协议(Online Certificate Status Protocol)来查询证书的有效性,它的工作方式是向 CA 发送查询请求,让 CA 返回证书的有效状态。
缺陷:OCSP 需要向 CA 查询,因此也是要发生网络请求,而且还得看 CA 服务器的“脸色”,如果网络状态不好,或者 CA 服务器繁忙,也会导致客户端在校验证书这一环节的延时变大。
1.2.3. OCSP Stapling
于是为了解决这一个网络开销,就出现了 OCSP Stapling,其原理是:服务器向 CA 周期性地查询证书状态,获得一个带有时间戳和签名的响应结果并缓存它。
当有客户端发起连接请求时,服务器会把这个「响应结果」在 TLS 握手过程中发给客户端。由于有签名的存在,服务器无法篡改,因此客户端就能得知证书是否已被吊销了,这样客户端就不需要再去查询。
会话复用
这种方式就是会话复用(TLS session resumption),会话复用分两种:
- 第一种叫 Session ID;
- 第二种叫 Session Ticket
1.1.1. Session ID
Session ID 的工作原理是,客户端和服务器首次 TLS 握手连接后,双方会在内存缓存会话密钥,并用唯一的 Session ID 来标识,Session ID 和会话密钥相当于 key-value 的关系。
缺点:
- 服务器必须保持每一个客户端的会话密钥,随着客户端的增多,服务器的内存压力也会越。
- 现在网站服务一般是由多台服务器通过负载均衡提供服务的,客户端再次连接不一定会命中上次访问过的服务器,于是还要走完整的 TLS 握手过程;
1.1.2. Session Ticket
直接把**「会话密钥」**加密
服务器不再缓存每个客户端的会话密钥,而是把缓存的工作交给了客户端,类似于 HTTP 的 Cookie。
- 对于集群服务器的话,要确保每台服务器加密 「会话密钥」的密钥是一致的,这样客户端携带 Ticket 访问任意一台服务器时,都能恢复会话。
- Session ID 和 Session Ticket 都不具备前向安全性,因为一旦加密「会话密钥」的密钥被破解或者服务器泄漏「会话密钥」,前面劫持的通信密文都会被破解
重放攻击:
避免重放攻击的方式就是需要对会话密钥设定一个合理的过期时间
1.1.3. Pre-shared Key
前面的 Session ID 和 Session Ticket 方式都需要在 1 RTT 才能恢复会话。
而 TLS1.3 更为牛逼,对于重连 TLS1.3 只需要 0 RTT,原理和 Ticket 类似,只不过在重连时,客户端会把 Ticket 和 HTTP 请求一同发送给服务端,这种方式叫 Pre-shared Key。
同样的,Pre-shared Key 也有重放攻击的危险。
所以,应对重放攻击可以给会话密钥设定一个合理的过期时间,以及只针对安全的 HTTP 请求如 GET/HEAD 使用会话重用。
总结
对于硬件优化的方向,因为 HTTPS 是属于计算密集型,应该选择计算力更强的 CPU,而且最好选择支持 AES-NI 特性的 CPU,这个特性可以在硬件级别优化 AES 对称加密算法,加快应用数据的加解密。
对于软件优化的方向,如果可以,把软件升级成较新的版本,比如将 Linux 内核 2.X 升级成 4.X,将 openssl 1.0.1 升级到 1.1.1,因为新版本的软件不仅会提供新的特性,而且还会修复老版本的问题。
对于协议优化的方向:
- 密钥交换算法应该选择 ECDHE 算法,而不用 RSA 算法,因为 ECDHE 算法具备前向安全性,而且客户端可以在第三次握手之后,就发送加密应用数据,节省了 1 RTT。
- 将 TLS1.2 升级 TLS1.3,因为 TLS1.3 的握手过程只需要 1 RTT,而且安全性更强。
对于证书优化的方向:
- 服务器应该选用 椭圆曲线(ECDSA)证书,而非 RSA 证书,因为在相同安全级别下,ECC 的密钥长度比 RSA 短很多,这样可以提高证书传输的效率;
- 服务器应该开启 OCSP Stapling 功能,由服务器预先获得 OCSP 的响应,并把响应结果缓存起来,这样 TLS 握手的时候就不用再访问 CA 服务器,减少了网络通信的开销,提高了证书验证的效率;
对于重连 HTTPS 时,我们可以使用一些技术让客户端和服务端使用上一次 HTTPS 连接使用的会话密钥,直接恢复会话,而不用再重新走完整的 TLS 握手过程。
常见的会话重用技术有 Session ID 和 Session Ticket,用了会话重用技术,当再次重连 HTTPS 时,只需要 1 RTT 就可以恢复会话。对于 TLS1.3 使用 Pre-shared Key 会话重用技术,只需要 0 RTT 就可以恢复会话。
这些会话重用技术虽然好用,但是存在一定的安全风险,它们不仅不具备前向安全,而且有重放攻击的风险,所以应当对会话密钥设定一个合理的过期时间。最好是只针对安全的 HTTP 请求如 GET/HEAD 使用会话重用
HTTP/2牛逼在哪里
1. HTTP/1.1协议的性能问题
- 延迟难以下降,虽然现在网络的「带宽」相比以前变多了,但是延迟降到一定幅度后,就很难再下降了,说白了就是到达了延迟的下限;
- 并发连接有限,谷歌浏览器最大并发连接数是 6 个,而且每一个连接都要经过 TCP 和 TLS 握手耗时,以及 TCP 慢启动过程给流量带来的影响;
- 队头阻塞问题,同一连接只能在完成一个 HTTP 事务(请求和响应)后,才能处理下一个事务;
- HTTP 头部巨大且重复,由于 HTTP 协议是无状态的,每一个请求都得携带 HTTP 头部,特别是对于有携带 Cookie 的头部,而 Cookie 的大小通常很大;
- 不支持服务器推送消息,因此当客户端需要获取通知时,只能通过定时器不断地拉取消息,这无疑浪费大量了带宽和服务器资源。
为了解决 HTTP/1.1 性能问题,举例几个常见的优化手段:
- 将多张小图合并成一张大图供浏览器 JavaScript 来切割使用,这样可以将多个请求合并成一个请求,但是带来了新的问题,当某张小图片更新了,那么需要重新请求大图片,浪费了大量的网络带宽;
- 将图片的二进制数据通过 Base64 编码后,把编码数据嵌入到 HTML 或 CSS 文件中,以此来减少网络请求次数;
- 将多个体积较小的 JavaScript 文件使用 Webpack 等工具打包成一个体积更大的 JavaScript 文件,以一个请求替代了很多个请求,但是带来的问题,当某个 js 文件变化了,需要重新请求同一个包里的所有 js 文件;
- 将同一个页面的资源分散到不同域名,提升并发连接上限,因为浏览器通常对同一域名的 HTTP 连接最大只能是 6 个;
而一些关键的地方是没办法优化的,比如请求-响应模型、头部巨大且重复、并发连接耗时、服务器不能主动推送等,要改变这些必须重新设计 HTTP 协议,于是 HTTP/2 就出来了!
2. 兼容HTTP/1.1
3. 头部压缩
4. 二进制帧
5. 并发传输
6. 服务器主动推送资源
7. 总结
HTTP/2 协议其实还有很多内容,比如流控制、流状态、依赖关系等等。
这次主要介绍了关于 HTTP/2 是如何提升性能的几个方向,它相比 HTTP/1 大大提高了传输效率、吞吐能力。
第一点,对于常见的 HTTP 头部通过静态表和 Huffman 编码的方式,将体积压缩了近一半,而且针对后续的请求头部,还可以建立动态表,将体积压缩近 90%,大大提高了编码效率,同时节约了带宽资源。
不过,动态表并非可以无限增大, 因为动态表是会占用内存的,动态表越大,内存也越大,容易影响服务器总体的并发能力,因此服务器需要限制 HTTP/2 连接时长或者请求次数。
第二点,HTTP/2 实现了 Stream 并发,多个 Stream 只需复用 1 个 TCP 连接,节约了 TCP 和 TLS 握手时间,以及减少了 TCP 慢启动阶段对流量的影响。不同的 Stream ID 可以并发,即使乱序发送帧也没问题,比如发送 A 请求帧 1 -> B 请求帧 1 -> A 请求帧 2 -> B 请求帧2,但是同一个 Stream 里的帧必须严格有序。
另外,可以根据资源的渲染顺序来设置 Stream 的优先级,从而提高用户体验。
第三点,服务器支持主动推送资源,大大提升了消息的传输性能,服务器推送资源时,会先发送 PUSH_PROMISE 帧,告诉客户端接下来在哪个 Stream 发送资源,然后用偶数号 Stream 发送资源给客户端。
HTTP/2 通过 Stream 的并发能力,解决了 HTTP/1 队头阻塞的问题,看似很完美了,但是 HTTP/2 还是存在“队头阻塞”的问题,只不过问题不是在 HTTP 这一层面,而是在 TCP 这一层。
HTTP/2 是基于 TCP 协议来传输数据的,TCP 是字节流协议,TCP 层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给 HTTP 应用,那么当「前 1 个字节数据」没有到达时,后收到的字节数据只能存放在内核缓冲区里,只有等到这 1 个字节数据到达时,HTTP/2 应用层才能从内核中拿到数据,这就是 HTTP/2 队头阻塞问题。
有没有什么解决方案呢?既然是 TCP 协议自身的问题,那干脆放弃 TCP 协议,转而使用 UDP 协议作为传输层协议,这个大胆的决定,HTTP/3 协议做了!
既然有HTTP协议,为什么还要有RPC?
使用纯裸 TCP 会有什么问题
问题(中间的思考)
SSL/TLS 协议中SSL和TLS分别是什么,这两个协议必须连在一起用吗 还是可以分开
SSL(Secure Sockets Layer):安全套接字层
TLS(Transport Layer Security):传输层安全协议
都是为网络通信提供安全和数据完整性的加密协议。它们都用于在两个通信应用程序之间提供保密性和数据完整性。
SSL是早期的协议版本,由网景公司在1994年开发。TLS是SSL的后续版本,1999年由互联网工程任务组(IETF)标准化。现在的许多所谓的“SSL证书”实际上是TLS证书。
SSL和TLS不需要一起使用,实际上在大多数情况下,它们不会同时使用。对于大部分应用场景,推荐使用TLS,因为相比SSL,TLS提供了更高的安全性。不过,二者并非完全互换,因为TLS 1.0并不能工作在SSL 3.0之上,对于某些需要向下兼容的情况,可能还需要使用SSL。
在安全性和加密处理上,TLS相比SSL有更多的优点和更强的安全性,因此现在TLS已经成为了主流,而SSL 3.0由于存在安全漏洞,在2015年被废弃。总的来说,SSL和TLS不是必须连在一起用,而且大多数情况下,我们使用的是TLS协议。
RSA 加密算法是什么
RSA加密算法是一种非对称加密算法,也是目前最知名的、广泛应用的公开密钥算法。它是由三个麻省理工学院的科学家,Ron Rivest、Adi Shamir 和 Leonard Adleman一起提出的。这三个人的姓氏首字母组成了RSA这个名字。
非对称加密算法有两把密钥:公钥和私钥。公钥与私钥是一对,使用公钥加密的信息只有用匹配的私钥才能解密,反之亦然。在公开和传输信息的过程中,公钥可以公开,而私钥需要严格保密。 RSA加密算法的主要优势在于:即使有人获取到加密的信息和使用的公钥,他们也无法破解出原始的消息,除非他们有匹配的私钥。
HTTP/2分那么多层的意义是什么
TCP报文-Steam-Message-Frame
确实,HTTP/2 将通信细分为多个层级:TCP 报文,Stream,Message 和 Frame。以下是每个层级的基本职责和一个示例:
- TCP 报文:这是最底层的网络通信层,负责在网络中传输原始数据包。是建立网络连接和确保数据正确传输的基础。比如,当你向 Google 发送一个搜索请求时,这个请求会被分解为多个 TCP 包,并在网络中进行传输。
- Stream:HTTP/2 引入的概念,是一种虚拟信道,可以并发发送多个请求和响应消息,提高了通信效率。每个 Stream 有一个唯一的标识符。比如,你在浏览一个网页时,文字,图像,CSS 和 JavaScript 文件可能会在不同的 Stream 中并发获取。 在Stream层控制请求的优先级
- Message:一个Message代表了一个完整的 HTTP 交互,可以是一个请求或一个响应。比如,你向服务器请求一个网页(一个请求消息),服务器回应这个请求并返回网页内容(一个响应消息)。
- Frame:在HTTP/2中,所有通信都以二进制格式的 Frame (帧) 进行。每个 Frame 都包含一部分 Message,以及 Frame 类型(如数据,头信息),和它所属的 Stream 的 ID。比如,一个大型的图片可能会被拆分为多个 Frame 来在网络中进行传输。
实例:
- H2中:浏览器的控制台中看到的数据是以Message为单位的。
- 开发中传递的数据,各个结构体\类\实例可能是以Frame的单位进行传递的(二进制)
这样分层的好处在于,每一层都可以独立工作,独立升级,并且职责明确,便于理解和控制。例如,我们可以在TCP层调优网络配置,在Stream层控制请求的优先级,Frame层则是HTTP/2中的核心,负责实现多路复用、流控制等特性。
OpenSSL库指什么,
OpenSSL主要用来做什么OpenSSL 是一个开源的软件库,提供了一系列加密和安全通信的功能。它可以用来实现安全通信协议(如 TLS/SSL),进行数据加密和解密,生成和管理数字证书,以及进行各种密码学操作。
主要用途包括:
- 实现安全通信协议:OpenSSL 提供了 TLS/SSL 协议的实现,使得应用程序可以通过加密和身份验证来保护网络通信的安全性,防止数据被窃听、篡改或伪造。
- 数据加密和解密:OpenSSL 提供了对称加密和非对称加密的实现,可以使用各种算法(如 AES、RSA、ECC)对数据进行加密和解密,保护数据的机密性。
- 数字证书管理:OpenSSL 提供了用于生成、签名、验证和管理数字证书的功能。数字证书用于身份验证和安全通信,可用于建立安全连接、进行身份认证和密钥交换。
- 密码学操作:OpenSSL 提供了各种密码学算法的实现,如哈希函数(如 MD5、SHA)、随机数生成器、签名算法(如 DSA、ECDSA)等,可以用于数据完整性校验、生成安全的随机数和数字签名。
总之,OpenSSL 提供了丰富的加密和安全通信功能,使得开发人员能够在应用程序中实现安全性和隐私保护。它广泛应用于 Web 服务器、网络应用程序、加密通信软件等领域。
TLS1.2和TLS1.3的区别
直观区别就是TLS1.2建立连接需要2RTT,TLS1.3需要1RTT
上图的右边部分就是 TLS 1.3 的握手过程,可以发现 TLS 1.3 把 Hello 和公钥交换这两个消息合并成了一个消息,于是这样就减少到只需 1 RTT 就能完成 TLS 握手。
怎么合并的呢?具体的做法是,客户端在 Client Hello 消息里带上了支持的椭圆曲线,以及这些椭圆曲线对应的公钥。
服务端收到后,选定一个椭圆曲线等参数,然后返回消息时,带上服务端这边的公钥。经过这 1 个 RTT,双方手上已经有生成会话密钥的材料了,于是客户端计算出会话密钥,就可以进行应用数据的加密传输了。
而且,TLS1.3 对密码套件进行“减肥”了, 对于密钥交换算法,废除了不支持前向安全性的 RSA 和 DH 算法,只支持 ECDHE 算法。
前向安全性
因为客户端传递随机数(用于生成对称加密密钥的条件之一)给服务端时使用的是公钥加密的,服务端收到后,会用私钥解密得到随机数。所以一旦服务端的私钥泄漏了,过去被第三方截获的所有 TLS 通讯密文都会被破解。
具有前向安全性的ECDHE
即使有个牛逼的黑客破解了某一次通信过程的私钥,其他通信过程的私钥仍然是安全的,因为每个通信过程的私钥都是没有任何关系的,都是独立的,这样就保证了「前向安全」。
ECDHE是什么?
ECDHE 是一个网络安全的密钥交换方法,这四个字母的全称如下:
- E: Elliptic Curve(椭圆曲线),意味着此协议使用了椭圆曲线密码学,它相较于传统算法提供了更高的安全性以及更小的密钥大小,这使得加解密操作更为高效。
- C: Cryptography(密码学),这意味着这个方法使用的是基于数学理论和计算机科学理论的加密算法来保护信息安全。
- DH: Diffie-Hellman(迪菲-赫尔曼),这是迪菲-赫尔曼密钥交换的名称,一个可以让双方在公开通道上生成共享密钥的方法,而不用事先就共享密钥进行安排。
- E: Ephemeral(短暂的),意味着在这个密钥交换协议中,密钥只被使用一次,然后就被丢弃。这增加了协议的安全性,因为即使某个会话的密钥被破解,攻击者也不能用它去解析别的会话的数据。
总的来说,ECDHE 是一种使用椭圆曲线密码学的短暂迪菲-赫尔曼密钥交换协议。
英雄联盟的通信过程
- 协议选择:《英雄联盟》主要使用 UDP 协议进行数据通信。理由是 UDP 协议轻量,传输速度快,更适合实时性要求较高的游戏。事实上,不仅《英雄联盟》,大多数竞技类游戏也会选择 UDP 作为主要协议(参考:《游戏之网络初篇》)。
- 客户端和服务器通信:当你在游戏中移动、攻击等行动时,客户端会生成一个行动信息的数据包,通过 UDP 协议发送给服务器。
- 网络优化:尽管 UDP 协议本身无法保证数据包的可靠传输,但《英雄联盟》会在应用层实现自己的重发和确认机制,尽量确保重要的指令能够正确传输。
游戏自定义的协议有以下优点:
-
- 高效:定制的协议可以根据游戏的实际需求进行优化,例如,它们可以使用紧凑的编码方案来压缩数据,从而减少网络带宽的使用。
- 灵活:游戏开发者可以根据需要随时更新协议,以适应游戏的更新和修改。
- 安全:可以通过设计协议来增加额外的安全机制,防止作弊。
- 服务器选择:为了提供更好的游戏体验,你登录游戏时会被自动分配到离你最近的服务器。这样可以减少网络延迟,使得游戏的响应更快。