- 本篇重点学习HTTPS, 顺带衍生最新HTTP最新的发展情况
1. HTTPS 超文本传输安全协议
HTTPS(超文本传输安全协议 HyperText Transfer Protocol Secure)是 HTTP 协议的加密版本。它使用 SSL 或 TLS 协议来加密客户端和服务器之间所有的通信。安全连接允许客户端与服务器安全地交换敏感数据,例如网上银行或者在线商城等涉及金钱的操作。-- HTTPS - MDN Web 文档术语表:Web 相关术语的定义 | MDN (mozilla.org)
HTTPS并不是一个单独的协议,而是对工作在一加密连接(TLS/SSL)上的常规HTTP协议的称呼。
即 HTTPS = HTTP + TLS/SSL(安全套接字层/传输层安全)
这说明HTTPS和HTTP除了加密报文增强安全性和保密性以外, 几乎没有任何差别. 对于HTTPS的学习, 我们仅需要关注和HTTP的不同点即可
1.1与HTTP的差异
HTTP的URL是由“http://”起始与默认使用端口80,而HTTPS的URL则是由“https://”起始与默认使用端口443。
HTTP不是安全的,而且攻击者可以通过监听和中间人攻击等手段,获取网站帐户和敏感信息等。HTTPS的设计可以防止前述攻击,在正确配置时是安全的。 (引用自维基百科)
1.2.工作原理
HTTPS本质上是将HTTP请求的报文明文通过TLS或者SSL加密后再在网络传输, 其实就是在传输前多了报文加密的步骤.
在对HTTP已经足够了解的情况下, 我们需要进一步聊这两个加密算法SSL和TLS.
1.2.0 TLS的性能优化与加密算法
加密算法通常分为两大类:对称加密和非对称加密。
-
对称加密算法:
- 在对称加密中,加密和解密使用相同的密钥。
- 优点是计算速度快,适合大量数据的加密。
- 缺点是密钥分发和管理较为困难,因为所有使用该密钥的人必须保密。
- 常见的对称加密算法包括:AES(高级加密标准)、DES(数据加密标准)、3DES(三重数据加密算法)等。
-
非对称加密算法:
- 非对称加密使用一对密钥,一个公钥用于加密,一个私钥用于解密。
- 优点是密钥分发简单,因为公钥可以公开,而私钥必须保密。
- 缺点是计算速度慢,不适合加密大量数据。
- 常见的非对称加密算法包括:RSA、DSA(数字签名算法)、ECC(椭圆曲线密码学)等。
加密算法涉及到新的领域, 是一个很大内容, 这里不进一步展开. 非对称加密安全性更高, 但是加解密效率不及对称加密. 所以TLS仅在握手环节使用非对称加密交换传输环节需要使用的密钥, 而在传输报文环节使用的是对称加密
1.2.1 SSL和TLS的关系
SSL是TLS的前身, SSL由网景公司提出经过了3个版本的迭代1.0、2.0、3.0(更新停止), IETF将SSL3.0标准化提出TLS, TLS也经过多个版本的迭代目前到1.3的版本. TLS具有更高的安全性, 目前主流的浏览器都基本支持了TLS协议, SSL是只有在浏览器等客户端不支持TLS的前提下的备用选项.
更多资料参考(需要科学上网): TLS - MDN Web 文档术语表:Web 相关术语的定义 | MDN (mozilla.org)
1.2.2 握手环节
通过握手环节, 可以TLS可创建一条加密的传输信道, 后面传输环节使用加密信道传输报文是安全的
- 当客户端连接到支持TLS协议的服务器要求建立安全连接并列出了受支持的密码包(包括加密算法、散列算法等),握手开始。
- 服务器从该列表中决定密码包,并通知客户端。
- 服务器发回其数字证书,此证书通常包含服务器的名称、受信任的证书颁发机构(CA)和服务器的公钥。
- 客户端确认其颁发的证书的有效性。
- 为了生成会话密钥用于安全连接,客户端使用服务器的公钥加密随机生成的密钥,并将其发送到服务器,只有服务器才能使用自己的私钥解密。
- 利用随机数,双方生成用于加密和解密的对称密钥。这就是TLS协议的握手,握手完毕后的连接是安全的,直到连接(被)关闭。如果上述任何一个步骤失败,TLS握手过程就会失败,并且断开所有的连接。
握手之后传输是安全的, 但是握手过程中的安全怎么保证呢?
1.2.2.1 数字证书
上面提到服务器需要给浏览器发回网站的数字证书. 在HTTPS中, 这个数字证书是每个网站(域名)独立的配置的(也就是数字证书和域名是一一绑定关系).
那证书怎么来呢?
- 我们可以使用OpenSSL创建自签名的SSL/TLS证书, 但是这样的证书是不被外界所承认的,一般用于测试环境或者内部网络.
- 只有公认的证书颁发机构签发的证书才被外界认可, 第二种方式就是需要找这样的机构来签发证书, 首先得准备一下自证的材料, 根据教程一步步来.
细节不展开了, 分享一篇教程: 无需花一分钱:轻松获取SSL证书的三种方法这篇精彩文章详细介绍了三种获取免费 SSL 证书的方式,让你轻松提升网站安全性 - 掘金 (juejin.cn)
1.2.2.2 证书颁发机构(CA)
不展开了, 来一篇百科: CA认证_百度百科 (baidu.com)
公认的证书签发机构会内置为操作系统的信任机构, 如果是自签发的证书, 那签发的机构是不会操作系统认可的, 如果在服务器上用了自签发证书, 在浏览器上访问该服务的时候, 会报签发机构不安全. 如果要解决这个问题, 要需将自签名的根证书添加到企业内部用户的操作系统和浏览器的信任列表中(每一台需要访问的客户端操作系统都需要装一遍,虽然有点麻烦, 但这在企业内很实用)
1.2.3 传输环节
握手环节建立好加密信道后, TLS提供报文加密和解密方法, 发送方对报文进行加密, 接收方对报文解密即可. 具体的实现和Web开发者无关, 服务器程序和浏览器提供支持.
1.3.如何将HTTP升级到HTTPS
讨论这个问题, 当时不是怎么给客户端(浏览器)升级HTTPS了, 因为现代主流的浏览器都支持HTTPS, 无需用户做任何操作. 而是作为一个WEB开发者, 这么让自己的网站支持HTTPS. 前提条件是你一个有一个HTTP的服务网站.
- 获取SSL的证书 (上面2.2.1已经给了一个教程) .
- 将证书配置到你的服务器上. 现在的服务器程序比如
Nginx、Apache等也是都支持HTTPS的, 这里给一个Nginx的配置教程: NGINX 路由配置与参数详解(https配置、跨域配置、socket配置)一、概述 Nginx 是一个高性能的开源We - 掘金 (juejin.cn)
2. HTTP/2
在HTTPS出现加强了HTTP的安全之后, 进一步的更新就是HTTP/2了. 参考文章:HTTP/2 - 维基百科,自由的百科全书 (wikipedia.org)
HTTP/2 简称h2, 默认即采用TLS1.2进行加密; 也有非加密的版本, 称为h2c,但主流客户端都不实现h2c的版本,导致其已被废弃.
HTTP/2相比HTTP/1.1的修改并不会破坏现有程序的工作,但是新的程序可以借由新特性得到更好的速度。
HTTP/2保留了HTTP/1.1的大部分语义,例如请求方法、状态码乃至 URI 和绝大多数HTTP头字段一致。而HTTP/2采用了新的方法来编码、传输客户端和服务器之间的数据。
2.1 新特性
所以我们关注HTTP/2只需要关注他和HTTP1.1有哪些不同即可.
- 性能改进HTTP/1.1中已有的多路复用,修复队头阻塞 (无需先入先出的多路复用)
- 允许设置设定请求优先级
- 新增头部压缩算法(HPACK)
- 报文采用二进制形式传输--帧、消息、流和TCP连接
- 服务器推送
重点关注HTTP/2对传输的改进, 向头部压缩等内容, 我们知道效果提升即可, 后续有时间再另开文章吧
2.1.1 消息-帧模式(HTTP/2传输)
HTTP/2将一个TCP连接分为若干个流(Stream),每个流中可以传输若干消息(Message),每个消息由若干最小的二进制帧(Frame)组成。HTTP/2中,每个用户的操作行为被分配了一个流编号(Stream ID),这意味着用户与服务端之间建立了一个TCP通道;协议将每个请求分割为二进制的控制帧与数据帧部分,以便解析。--维基百科
这里直接把请求分割为二进制控制帧与数据帧的表述不太准确, 容易引起歧义且和上一段描述也不相符.
HTTP/2 客户端对同一个域名, 只保持一个TCP连接, 但可能换新的(HTTP1.1也支持TCP连接多路复用,但存在阻塞问题.对于并发请求, 还是需要多个TCP连接).
用户的操作行为被分配一个流Stream(HTTP1.1中每一个行为对应一次请求,包含响应), 所以流Stream和请求对应, 流再细分为消息Message, 消息再细分才是帧Frame.
图片来源:HTTP/2 协议(帧、消息、流简单的抓包分析)-阿里云开发者社区 (aliyun.com)
流Stream
每一个流有自己唯一的编号
Stream ID为0的流仅用于传输控制帧- 由客户端建立的
Stream ID必须是奇数。 - 由服务端建立的
Stream ID必须是偶数(如服务端主动向客户端推送消息)。 - 新建立的
Stream ID必须大于曾经建立过的状态为opened或reserved的Stream ID。 - 在新建立的流上发送帧时,意味着将更小
ID且为idle状态的Stream设置为Closed状态。 Stream ID不能复用,长连接耗尽ID应创建新连接。- 流可以源源不断地并发传送,同一个
Stream流中的frame数据是串行发送的。
消息Message
消息分为请求、响应, 由一个或多个 Frame 组成.
帧Frame
帧(frame)包含部分:类型Type, 长度Length, 标记Flags, 流标识Stream和frame payload有效载荷。
帧存在以下类型:
| 帧类型 | 类型编码 | 含义 |
|---|---|---|
| DATA | 0x0 | 传递 HTTP 包体 |
| HEADERS | 0x1 | 传递 HTTP 头部 |
| PRIORITY | 0x2 | 指定 Stream 流的优先级 |
| RST_STREAM | 0x3 | 终止 Stream 流 |
| SETTINGS | 0x4 | 修改连接或者 Stream 流的配置 |
| PUSH_PROMISE | 0x5 | 服务端推送资源时描述请求的帧 |
| PING | 0x6 | 心跳检测,兼具计算 RTT 往返时间的功能 |
| GOAWAY | 0x7 | 优雅的终止连接或者通知错误 |
| WINDOW UPDATE | 0x8 | 实现流量控制 |
| CONTINUATION | 0x9 | 传递较大 HTTP 头部时的持续帧 |
SETTINGS类型的帧用于控制流的行为
| SETTINGS 帧字段 | 含义与作用 |
|---|---|
| SETTINGS_HEADERS_TABLE_SIZE(0x1) | 通知对端索引表的最大尺寸(单位字节,初始 4096 字节) |
| SETTINGS_ENABLE_PUSH(0x2) | Value 设置为 0 时可禁用服务器推送功能, 1 表示启用 |
| SETTINGS_MAX_CONCURRENT_STREAMS(0x3) | 告诉接收端允许的最大并发 Stream 数量 |
| SETTINGS_INITIAL_WINDOW_SIZE(0x4) | 声明发送端的窗口大小,用于 Stream 级别流控,初始值 2^16-1,即 65535字节 |
| SETTINGS_MAX_FRAME_SIZE(0x5) | 设置帧的最大大小,初始值 2^14,即 16384 字节 |
| SETTINGS_MAX_HEADER_LIST_SIZE(0x6) | 知会对端头部索引表的最大尺寸,单位字节,基于未压缩前的头部 |
服务器推送的消息有没有独立的类型呢?
帧标志Flags
在HTTP/2中,不同的帧类型会有不同的标志位(Flags),以下是一些常见帧类型及其对应的标志位含义:
-
DATA帧:
END_STREAM:表示这个帧是当前流的最后一个帧,用于告知对方流已经结束。PADDED:表示这个帧被填充(padding),用于流量分析保护或满足最低帧大小要求。
-
HEADERS帧:
END_STREAM:同DATA帧。END_HEADERS:表示这是HEADERS块的最后一个帧。PRIORITY:表示这个帧包含优先级信息。PADDED:同DATA帧。COMPRESSED:表示HEADERS被HPACK压缩。
-
PRIORITY帧:
- 没有特定的标志位。
-
RST_STREAM帧:
- 没有特定的标志位。
-
SETTINGS帧:
ACK:这个标志位表示SETTINGS帧是一个响应,用于确认收到对方的SETTINGS帧。
-
PUSH_PROMISE帧:
END_HEADERS:同HEADERS帧。PADDED:同DATA帧。
-
PING帧:
ACK:同SETTINGS帧。
-
GOAWAY帧:
- 没有特定的标志位。
-
WINDOW_UPDATE帧:
- 没有特定的标志位。
关于一些实现的细节
在具体的实现中, 并没有一个所谓消息Message存储结构, 甚至所谓的流Strame的存储结构也没有, 在TCP传输信道上, 就是一个个的数据帧.
区分流Stream: 怎么区分这个一个个的数据帧属于哪一个流呢? 刚说的流ID作用就来了, 每一个帧都有一个唯一的流ID, 具有相同的流ID的帧属于同一个流.
区分消息Message: 那又怎么区分消息呢? 在确认好帧的哪一个流后, 在流内部, 帧必须是按顺序传输的. 这样的话, 一个流只能同时处理一条信息. 然后通过帧的标志位Flags, 例如一条消息包含多个HEADRES帧, 当HEADRES帧的Flags为END_HEADERS时, 表示当前消息的头部传输结束; 下一步接着传输消息的数据载荷, 也是会包含多个DATA帧,当DATA帧的Flags为 END_STREAM则表示当前消息传输完成, 接着传下一条消息.
2.1.2 请求优先级
把http消息分为很多独立帧之后,就可以通过优化这些帧的交错和传输顺序进一步优化性能。每个流都可以带有一个31比特的优先值:0 表示最高优先级;2的31次方-1 表示最低优先级。
服务器可以根据流的优先级,控制资源分配(CPU、内存、带宽),而在响应数据准备好之后,优先将最高优先级的帧发送给客户端。高优先级的流都应该优先发送,但又不会绝对的。绝对地准守,可能又会引入首队阻塞的问题:高优先级的请求慢导致阻塞其他资源交付。
分配处理资源和客户端与服务器间的带宽,不同优先级的混合也是必须的。客户端会指定哪个流是最重要的,有一些依赖参数,这样一个流可以依赖另外一个流。优先级别可以在运行时动态改变,当用户滚动页面时,可以告诉浏览器哪个图像是最重要的,你也可以在一组流中进行优先筛选,能够突然抓住重点流。
- 优先级最高:主要的html
- 优先级高:CSS文件
- 优先级中:js文件
- 优先级低:图片
2.1.3 服务器推送
服务器可以对一个客户端请求发送多个响应,服务器向客户端推送资源无需客户端明确地请求。并且,服务端推送能把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。
正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度。Server Push 让 http1.x 时代使用内嵌资源的优化手段变得没有意义;如果一个请求是由你的主页发起的,服务器很可能会响应主页内容、logo 以及样式表,因为它知道客户端会用到这些东西,这相当于在一个 HTML 文档内集合了所有的资源。
不过与之相比,服务器推送还有一个很大的优势:可以缓存!也让在遵循同源的情况下,不同页面之间可以共享缓存资源成为可能。
注意两点:
1、推送遵循同源策略;
2、这种服务端的推送是基于客户端的请求响应来确定的。
当服务端需要主动推送某个资源时,便会发送一个 Frame Type 为 PUSH_PROMISE 的 Frame,里面带了 PUSH 需要新建的 Stream ID。意思是告诉客户端:接下来我要用这个 ID 向你发送东西,客户端准备好接着。客户端解析 Frame 时,发现它是一个 PUSH_PROMISE 类型,便会准备接收服务端要推送的流。
2.2 怎么用上HTTP/2?
-
从客户端(浏览器)角度看, 用户无需作任何操作, 现在主流的浏览器都支持HTTP/2, 只要你访问的服务支持即可
-
开发者从源码级别就可以使用HTTP/2:
HTTP/2相关SDK: Implementations · httpwg/http2-spec Wiki (github.com) -
对于静态资源服务器, 现有HTTP的网站就可以不需要修改源码迁移到HTTP/2, 这里还是以Nginx举例: 升级 Nginx 开启 HTTP/2HTTP 2.0即超文本传输协议 2.0,是下一代HTTP协议。是由互联网工程任务组 - 掘金 (juejin.cn)
引用文章说明
2.1.2 请求优先级、2.1.3 服务器推送 引用自深入理解http2.0协议,看这篇就够了! - 知乎 (zhihu.com), 文章对HTTP/2的介绍很好,可以跳过去看哦
HTTP/3
HTTP/3相对于HTTP/2,主要做的是底层优化,底层从TCP协议切换为QUIC协议,QUIC是一个最初由Google公司研发基于UDP协议研发的新协议,最终协议是由互联网工程任务组(IETF)中更广泛的团队设计。
推荐一篇好文:# 深入解析 HTTP/3 协议, 写得很有深度, 这里就不班门弄斧了