%%
# 纲要
> 主干纲要、Hint/线索/路标
# Q&A
#### 已明确
#### 待明确
> 当下仍存有的疑惑
**❓<font color="#c0504d"> 有什么问题?</font>**
# Buffer
## 闪念
> sudden idea
## 候选资料
> Read it later
%%
# HTTP 协议
> **HTTP(Hypertext Transfer Protocol) 超文本传输协议**
HTTP 协议是用于互联网上 **Web 客户端(如浏览器)** 与 **Web 服务器** 之间通信的协议,基于该协议可传输各类型的数据,包括文本、图像、音频、视频等多媒体内容。
其**规定/定义**了:
- **协议的工作模式**:**==客户端/服务器==模式**
- HTTP 协议工作在**客户端-服务端**架构上,**基于 TCP 协议**来传输数据,通信过程表现为 "**==请求-响应==**" 的过程
—— HTTP 客户端(浏览器)向 HTTP 服务器发送 "**HTTP 请求报文**",服务器处理请求并回发 "**HTTP 响应报文**"。
- **报文消息的内容格式**
- HTTP 协议规定了请求、响应报文中所应具有的**字段、内容格式**。
- **请求方法与状态码**
- 定义了 **一系列 HTTP 请求方法**(如 GET、POST、PUT、DELETE)来指示客户端**请求服务器所执行的动作**。
- 定义了 **一系列 HTTP 状态码**(如 200 OK、404 Not Found)来表示**服务器的响应状态**。
<br><br>
# 关于 "短连接" 与 "长连接"
- **==短连接==**(非持久连接):每个 "**请求/响应对**" 经由一个**独立的 TCP 连接**发送。
- **==长连接==**(持久连接):多个 "**请求/响应对**" 经由**同一个 TCP 连接**发送。
> [!NOTE] HTTP 头部中的 `Connection` 字段控制指定了连接方式:**短连接/长连接**。
>
> - HTTP 1.0 默认采用**短连接**,可使用请求头 `Connection: keep-alive` 要求保持长连接,直至某一方主动关闭。
> - HTTP 1.1 默认采用**长连接**,可使用请求头 `Connection: close` 表示使用短连接,服务器端则应在回发 HTTP 响应后主动关闭连接。
> - HTTP 2 中,连接复用是内置特性,**所有请求共享同一个 TCP 连接**。
>
> [!info] 在 "**短连接**" 下,通常都是由 "**==服务器端==**" 主动关闭连接(由 "请求-响应"的末端关闭)
>
> - **客户端**首先请求建立 TCP 连接,**而后发送请求报文**,**等待服务器响应**。
> - **服务器**完成请求处理,**回发响应报文后,==主动关闭 TCP 连接==**。
>
> 若客户端不需要服务器的响应,则也可在发送完请求后,主动断开连接。
> [!example]
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-11DEA2206456599CDA0FA03020F18E73.png|355]]
<br><br><br>
# HTTP 协议版本差异总结
- **HTTP/0.9**
- 只有 GET 方法
- **HTTP/1.0**:
- 明确基本的 HTTP 报文格式
- **明确请求行、响应行**(包括标识版本号、请求方法、响应状态码)
- **增加了 HTTP 请求方法**:引入 HEAD、POST 等新方法
- **增加了 HTTP 响应状态码**
- **引入了 HTTP 头部**(即请求头、响应头):支持使用头部字段(**键值对**)来传输元数据;
- 默认为 **==短连接==**(但支持使用 `Connection: keep-alive` 指定长连接)
- 引入 `Content-Type` 头部:传输数据不再仅限于文本
- **HTTP/1.1**
- 默认采用 ==**长连接**==;
- 支持**管道化请求**(pipelining):同一 TCP 连接上,客户端**可连续发起多个请求,而无需等待前一请求的响应**
- 支持**分块传输编码**(chunked transfer encoding)
- **改进缓存控制机制,引入了更多状态码**
- **HTTP/2**
- **基于二进制帧进行传输**
- **==多路复用==**(Multiplexing)
- **头部压缩**
- **支持服务器推送**
- **支持流优先级**
- **强制要求加密通信**(TLS 1.2 or 1.3)
- **HTTP/3**
- **弃用 TCP 协议,改用基于 UDP 协议的 ==QUIC 协议==**。
- **快速连接建立**:QUIC 减少了握手步骤,允许更快的连接建立
- **加密传输**:QUIC 内建了类似 TLS 的加密机制,所有传输数据都经过加密,提供了与 HTTPS 类似的安全保障。
> [!NOTE] HTTP 协议层次图
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-0DC61D86A0D253C3B2EEFBEFE777AAC9.png|649]]
>
<br>
# HTTP/1.0
> 推出时间:1996 年,正式定义于 [RFC 1945]
缺点:默认为 "**短连接**",**服务器==处理完请求后即断开连接==**,因此每进行一次数据请求("**请求-响应**")都要经过 "**三次握手 & 四次挥手**",传输低效。
# HTTP/1.1
> 推出时间:1997 年,最新修订于 RFC 7230 至 7235(2014 年)
新增内容:
- 默认采用 ==**长连接**==;
- 支持**管道化请求**(pipelining):同一 TCP 连接上,客户端**可连续发起多个请求,而无需等待前一请求的响应**
- 支持**分块传输编码**(chunked transfer encoding)
- **改进缓存控制机制,引入了更多状态码**
缺点:
- **==不安全==**:
- **明文传输**:报文是纯文本形式,明文传输,账户信息可被窃取;
- **无验证机制**:不验证通信双方身份,可能访问假冒网站;
- **无完整性校验**:无法验证报文完整性,可能获取**被篡改的报文**(例如网页上植入垃圾广告)
- **存在==队头阻塞==问题**,需要 **==并发多个 TCP 连接==来提升传输效率**;
- **无法压缩头部**,存在不必要的网络开销;
- `Content-Encoding: gzip` 头部只是指定了**对 "消息体" 的压缩方式**,对于请求头/响应头不会进行压缩。
- **不支持服务器推送**,只能由客户端发送请求,服务器**被动响应**。
> [!example] "长连接" 与 "管道化" 示例
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-F4D95D2AEE3E6B560FDC852515951CBF.png|615]]
<br>
### 🚨 队头阻塞问题(HOL Blocking)
HTTP/1.1 中默认采用 "**长连接**",在此情况下可能存在 "**队头阻塞问题**"——即**前面的请求延迟**会阻塞后面请求的处理。
HTTP 采用 "**请求-响应**" 的工作模式,在**收到上一请求的应答后才会发送下一个请求**。
示例场景:一个 Web 页面的加载**如果仅使用一个 TCP 连接**,假设该 Web 页面包括 HTML 基本页面,页面中包含**大视频片段**,视频下方有多个小对象。在请求过程中,若视频片段需要花费较长时间来进程传输,则其后的小对象都将被阻塞并等待。
> [!NOTE] HTTP/1.1 中解决该问题的典型方式是 **==打开多个并行 TCP 连接==**(通常 6~8 个),**分别进行资源请求**。
> [!example] 队头阻塞示例
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-0D4739956A84D83340CA1F723367455E.png|333]]
<br>
### 管道化传输
管道传输:同一 TCP 连接上,客户端**可连续发起多个请求,而无需等待前一请求的响应**。
> [!caution] 由于队头阻塞问题,浏览器实现上几乎都没有支持 "**管道传输**"。
>
> "管道化传输" 并没有解决 "**==队头阻塞==**" 问题。
> 尽管允许客户端连续发送多个请求,但由于**服务器端仍是==按请求顺序==响应**的,若**处理先前请求耗时较久**,同样则会**阻塞对后续请求的处理**。
>
> 因此,仍然存在**响应报文的 「==队头阻塞==」问题**。
<br>
### 分块传输编码
该机制下,支持**将响应数据分成多个部分(==块==)进行发送**,而不是一次性发送整个响应,**适用于响应的总长度事先未知的情况,如动态生成的内容或流媒体**。
实现方式:
1. **标识 `Transfer-Encoding: chunked` 头部**,指明采用**分块传输**方式;
2. **去除 `Content-Length` 头部**,因为长度大小不固定;
3. 消息体中,每个**块**由两部分构成:「**块大小(十六进制值)**」与 「**实际数据**」
- **首行**为一个 16 进制值,标识**当前块的字节数**;
- 当 **"块大小" 标识为 0** 时,**标记==结束==**
- **第二行**起为实际数据。
```txt
每个Chunk块的格式如下:
<Chunk Size in Hex>\r\n
<Chunk Data>\r\n
结束块的格式:
0\r\n
\r\n
```
> [!example] "分块传输编码" 报文示例
>
> 传输的原消息内容为:"Wikipedia in Chunks."
>
> ```HTTP
> HTTP/1.1 200 OK\r\n
> Content-Type: text/plain\r\n
> Transfer-Encoding: chunked\r\n
> \r\n
> 4\r\n
> Wiki\r\n
> 5\r\n
> pedia\r\n
> E\r\n
> in\r\n
> chunks.\r\n
> 0\r\n
> \r\n
> ```
<br><br>
# HTTP/2
> **推出时间**:2015 年,定义于 [RFC 7540]
新增内容:
- 基于 **==二进制帧==** 进行传输
- **多路复用**:通过标识**每个帧所属的流**,基于**帧的交错传输**,实现单个 TCP 连接上多个流的 "**==并发传输==**"。
- **头部压缩**:通过 **==HPACK 算法==** 来**编码头部帧**
- **支持服务器推送**;
- **支持流优先级**:帧首部可以设置流的优先级,允许高优先级资源优先传输;
- **强制要求通信加密**(SSL/TLS)
## 二进制帧传输
> [!NOTE] "**二进制表示**" 的含义
>
> - HTTP/1.1:数据在应用层**以"纯文本字符串"的形式直接传输**(传输时传输的是文本对应的 ASCII 码);
> - HTTP/2:数据在应用层直接以 "**特定二进制编码** 表示,从而允许更快的解析速度,更小的消息大小,以及更复杂的通信控制(如流、帧、优先级等)。
>
> 例如对于状态码 `200`:
>
> - HTTP/1.1 以**纯文本方式**表示,表示为 `'2','0','0'` **三个字符**,**共需要 3 个字节**。
> - HTTP/2 的**二进制编码**下,**直接采用二进制编码 `10001000`** 表示状态码 `200`,**只需 1 个字节**。
>
HTTP/2 中,HTTP 报文采用 "**二进制格式**" 表示,对 "**头部**" 和 "**数据**" 进行了拆分,**分别封装为独立的==二进制帧==**(采用特定二进制编码),
- **头部帧**(Headers Frame)
- **数据帧**(Data Frame)
封装为**二进制帧**后,再进一步 **采用 ==HPACK 算法==进行压缩**,实现高效传输。
接收端需要通过 HTTP/2 的解析算法**在应用层重新组合二进制帧并解码得到 HTTP 报文的文本内容**。
![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-8BEC791E12294882EAAE7EA045648202.png|542]]
### 二进制帧的结构
每个二进制帧的结构如下,**共 10 种帧类型**,大体分为两类:「**数据帧**」与 「**控制帧**」。
其中标志位 8 位,携带简单控制信息,例如
- `END_HEADERS`:**头数据结束标志**;
- `END_STREAM`:**单方向数据发送结束**,后续不再有数据帧
- `PRIORITY`:**表示流的优先级**
![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-2B330430013BC16D1313C328A675D07C.png|928]]
<br>
## 多路复用(帧交错传输)
HTTP/2 基于 "**二进制帧的交错传输**" 实现多路复用,支持在**单个 TCP 连接上==并发==进行多个请求和响应**,旨在解决 HTTP/1.1 中的队头阻塞问题。
HTTP/2 在一个 TCP 连接中引入了 "==**流**=="(stream)的概念:
- **每个 HTTP ==请求-响应对==** 视为一个 **==独立的流==**(双向通信通道),具有**唯一标识的流 ID**。
- **每个二进制帧** 属于 "**一个特定的流**"(帧头部的 "**==流标识符==**" 字段指示)
即 **HTTP 请求或响应**会拆分为**多个 "帧"**,每个帧标识属于一个特定流,**帧会==交错发送==**,**接收端基于流 ID ==重新组装帧==得到完整 HTTP 报文**。
> [!NOTE] 多路复用示意
>
> 单个 TCP 连接上,实际数据传输过程表现为:**帧的交错发送**
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-1A3C12347C584FB39019045FB1E5E1D0.png|579]]
>
> 由于传输的每个帧具有 "**流标识**",因此从**逻辑上**来看,表现为**单个 TCP 连接上存在多个彼此独立的流**,如下所示:
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-975E4ECF16196B6B23AACC0FE1BA5E78.png|376]]
>
>
> [!example] 示例说明
>
> 在单个 TPC 连接下,假设 Web 页面有一个视频片段和 8 个小对象,**视频片段由 1000 帧构成,每个小对象由 2 帧构成**。
> 在**帧交错机制**下,视频片段发送一帧后,其余每个小对象依次发送一帧,因此**在视频片段发送完 18 帧后,所有小对象的传输就已经完成了**。
>
> 如果是在 HTTP/1.1 中,意味着单个 TPC 连接下,**必须先传输完视频片段的 1000 帧**,才轮到小对象们的 18 帧。
<br>
## 服务器推送
基于**帧交错传输**的机制,服务器可以**主动向客户端发送消息**(**主动建立流**),唯一要求是:
- **客户端**建立的 Stream ID 为**奇数**;
- **服务器**建立的 Stream ID 为**偶数**。
由此,服务器可以主动推送消息,可有助于减少消息传递次数。
> [!example] 示例:服务器主动推送
>
> HTML 基本页面指示了需要在页面呈现的全部对象,因此**服务器可以分析 HTML 基本页**,识别客户端需要的对象 (如页面中的图片、视频等),**在接收到对这些对象的请求前就将其发送到客户端**。
>
> 例如,客户端通过 HTTP/1.1 请求从服务器获取 HTML 文件,而 HTML 可能同时依赖于 CSS 渲染页面,故在 HTTP/1.1 中客户端共需要分两次请求。
> 而在 HTTP/2 中,服务器**可在返回 HTML 的同时**,**主动推送另一个报文来返回相关联的 CSS 文件**。
>
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-3B013642DF580DE1C370AA808050AA5C.png|586]]
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-475C6C24E42E783F5C58B010A2850A20.png|581]]
>
<br>
## HPACK 压缩
HTTP/2 中采用 HPACK 算法来进行 **==头部帧==的编码**,从而实现**头部压缩**,减少**冗余头部信息**。
#### HPACK 工作原理
HPACK 引入了**两个表**,记录**头部字段信息**:
- **静态表**:客户端和服务器中内置的**固定表项**,预定义了 **常见 HTTP 头部字段名称和值**(如 `:method`、`:path`、`:scheme` 等)。
- **动态表**:客户端和服务器**在连接期间维护的动态表**,存储在 **会话过程中** 出现的新头部字段。
- 大小有限,基于 FIFO 机制,表满时最早的表项会被移除;
基于两个表,HPACK 综合两种方式来编码头部字段:
1. **索引表示**
- 当某个头部字段(包括名称和值)已位于**静态表或动态表**中时,直接**取其在==表中的索引值==**(作为**帧中的二进制编码**)
2. **字面表示**
- 当头部字段是新的或者不适合索引时,**直接传输其==字面值==**(文本形式),同时可选择是否将该头部字段添加到动态表中。
- **带索引的字面表示**:传输时会指明该头部字段需要加入动态表。
- **不带索引的字面表示**:仅传输原始内容,不更新动态表。
对于所得的二进制编码,进一步应用 **==Huffman 编码==** 实现数据压缩。
> [!example] HPACK 编码示例
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-2958D32E3EE016CF285FC3D67B07C4DE.png|487]]
>
> 例如**状态码 `200`**,在 HPACK 编码后为 1 字节 8 位:10001000,其中:
>
> - **首位**:为 1,标识其为 "**静态表**" 中已存在的 KV;
> - **剩余 7 位**:为 0001000,即该项**在静态表中的索引值 8**。
>
<br>
## 🚨 TCP 层面的队首阻塞问题
由于**多个流复用同一 TCP 连接**,因此虽然逻辑上看似独立,但**由于受 TCP 可靠性传输的保证**,实际也可能存在**队头阻塞**问题。
TCP 提供可靠性传输,因此**如果出现==中间 TCP 报文丢包==**,则**后续接收到的 TCP 报文将==暂存在接收缓冲区==**,**直至构成完整的连续数据后才进行交付**。
因此,HTTP/2 在基于 TCP 传输的过程中,如果 **载有中间某个流的帧** 丢失,**将阻塞交付其他流的帧**。
> [!example]
>
> 4 个帧属于不同流,其中 **stream2 流的帧丢失**,导致属于 streaam4 与 stream3 的帧也无法被交付。
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-BB94D9C08DD828D06281008884055FDD.png|659]]
>
<br><br>
# HTTP/3
> **推出时间**:22 年 6 月 6 日,IETF 正式标准化 HTTP/3 为 [RFC 9114]
核心差异: **不再基于 TCP 连接,改为基于 UDP 通信的 ==QUIC 协议==进行传输**
> [!quote] 支持 HTTP3 的 Web 服务器
>
> - [LiteSpeed Web Server](https://zh.wikipedia.org/w/index.php?title=LiteSpeed_Web_Server&action=edit&redlink=1)(及 OpenLiteSpeed)6.0.2 版本于 2021 年 6 月 7 日发布,并成为默认启用 HTTP/3 的首个版本。
> - [Nginx](https://zh.wikipedia.org/wiki/Nginx) 自 1.25.0 版本(2023 年 5 月 23 日发布)开始支持 HTTP/3。
> - 2020 年 6 月,发布了支持 HTTP/3 的 nginx 技术预览版。
> - 2023 年 2 月,发布了支持 HTTP/3 的 nginx 二进制包。
> - [Caddy](https://zh.wikipedia.org/wiki/Caddy) 网页服务器 v2.6.0(2022 年 9 月 20 日发布)默认启用了 HTTP/3。
> - [Microsoft IIS](https://zh.wikipedia.org/wiki/網際網路資訊服務) 在 Windows Server 2022/Windows 11 上原生支持 HTTP/3。
<br>
## QUIC 协议
> QUIC:**快速 UDP 互联网连接**协议。由 Google 于 2012 年开发,并在互联网工程任务组 (IETF) 创建新的 HTTP/3 标准时采用。
**QUIC 协议是一个基于 UDP 的==应用层==协议**,旨在**解决 TCP 通信的一些限制,例如慢启动、连接时延和队头阻塞**,从而提高网络通信的速度和效率,特别是在具有高延迟和丢包的网络环境中。
QUIC 协议的关键特性:
- **多路复用**:QUIC 基于 UDP,允许**多个独立的流同时在单个 QUIC 连接上进行**,而**不会受到 TCP 的队头阻塞问题**。
- **快速连接建立**:内置 TLS 1.3,**QUIC 三次握手过程本身即包含 TLS 握手信息**,1-RTT 内完成 **==连接建立 & 加密协商==**。
- **加密传输**:内置 TLS 1.3,所有传输数据**必然加密**。
### QUIC 多路复用
QUIC 采用了与 HTTP/2 stream 类似的多路复用思路,在同一连接上并发传输多个 stream。
由于 QUIC 是**基于 UDP 的**,因此**不存在 TCP 的队头阻塞问题**,当属于某个流的数据包丢失时,**==仅阻塞该流==**。
> [!example] QUIC 无队头阻塞
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-E1F5FD1DC93684182B252B9D86EED09D.png|504]]
<br>
### QUIC 快速连接建立
QUIC 内置了 TLS 1.3,**QUIC 三次握手过程即包含 TLS 握手信息**,可在 1-RTT 内完成**连接建立和加密协商**,**无需额外的 TLC 握手过程**
> [!NOTE] HTTPS 对比 HTTP3 的连接过程
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-D79D14FC4185EC725FE37F24A49DBE87.png|621]]
>
<br><br><br>
# HTTPS 协议
> HTTPS 协议(HTTP Secure)
HTTPS 是在 **HTTP 通信** 的基础上**利用 ==SSL/TLS 协议== 进行加密**,涉及两方面:
1. **建立连接时**:https 多了 **TLS 的握手过程**(TLS 1.2 是 4 次握手,TLS 1.3 是 3 次握手)
2. **传输数据时**:https 对**传输数据**进行加密(**对称加密**)
HTTPS 提供了三方面的安全保障功能:
1. **身份认证**:由服务器网站提供**可信 CA 签发的数字证书**,交由客户端浏览器验证,确认**访问网站安全**。
2. **数据加密**:所有传输数据进行**加密**,防止数据被窃取。
3. **完整性校验**:所有传输数据进行**完整性校验**,防止数据被篡改。
详细说明参见 [[02-开发笔记/07-计算机网络/传输层/SSL & TLS 协议|SSL & TLS 协议]]
![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-8A17A3DCB846A8D02EDE8365462D09C1.png|470]]
> [!NOTE] **HTTPS** = HTTP + **SSL/TLS** (服务器身份认证 + 通信加密 + 完整性校验)
>
> HTTPS 并非是应用层的一种新协议,只是**在 HTTP 与 TCP 之间引入 SSL/ TLS 协议进行加密**。
>
>
> 
>
> [!info] HTTPS 的默认端口
>
> - HTTP 的 URL 是由 `http://` 起始,默认使用端口 **80**;
> - HTTPS 的 URL 则是由`https://` 起始,默认使用端口 **443**。
<br>
## HTTPS 连接建立过程
首先进行 **TCP 三次握手**,再进行 **==TLS 握手==**(TLS 1.2 是 4 次握手,TLS 1.3 是 3 次握手)。
(TCP 第三次握手可携带 TLS 第 1 次握手的报文)
建立 HTTPS 连接要求**双端均实现 HTTPS 协议**,需要双端成功完成 **TLS 握手过程**,即:
- **双端必须支持一套兼容的 TLS 版本**;
- **客户端成功验证服务器的 SSL/TLS 证书**;
若该过程出现任何问题(如 TLS 版本不兼容、证书不可信等),则**浏览器将阻止建立安全连接**,并可能显示一个警告消息。
> [!NOTE] HTTPS 连接建立示意
>
> 下图以 **TLS 1.2** 为例,共包含 4 次握手。
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-05C187F6C8041BAAECEBBB09A6C4A68F.png|771]]
<br><br>
## HTTPS 的安全性
HTTPS 基于 TLS 协议,**本身足够安全**。
尽管可能存在**中间人攻击**,例如**客户端发给服务器的 HTTPS 连接请求被劫持**,转发到一个**中间人服务器**。
但由于**中间人服务器本身伪造的证书**是**可被浏览器识别==不可信==的**,因此可以**防止连接建立**(除非用户执意接受该不可信证书)。
> [!faq] ❓抓包工具获取 HTTPS 明文内容亦是通过 "**==中间人攻击==**"
>
> 抓包工具安装时会向系统里注入**自签名的 CA 根证书**,从而能够充当 "**受信任的中间人**",以便**拦截 HTTPS 报文并进行解密**,然后再转发。
>
> 当抓包工具(例如 Fiddler、BurpSuite)启用 **HTTPS 拦截**功能时,其实际上运行了一个 "**==本地代理服务器==**,**监听在 `127.0.0.1` 的某端口**。
>
> 因此,为浏览器设置**代理模式**,将 HTTP 和 HTTPS 流量代理到**上述地址和端口**,
> 则浏览器或任何 HTTPS 客户端将**与抓包工具建立一个 TCP + TLS 连接**,再由抓包工具与 "**目标服务器**" 建立另一个 TCP +TLS 连接。
> 由此,抓包工具可拦截 HTTPS 内容。
>
> ```text
> 浏览器 <----(TLS)----> 抓包工具 <----(TLS)----> 目标服务器
> (本地代理) (真实的 TLS 连接)
> ```
>
>
> [!note] "**代理服务器**" 可作为 "**==中间人服务器==**",监控和拦截 HTTPS 传输数据。
>
> 例如,企业或学校可将其 "**代理服务器**" 作为 "**==中间人服务器==**",在其内部电脑上**安装自建 CA 证书**(用以**认证代理服务器**),
> 由此可通过 "**代理服务器**" ==**监控&解密==所有 HTTPS 传输内容、拦截 HTTPS 访问**。
> [!example] 中间人攻击示例
>
> 下列情况成立的前提是:
>
> 1. **客户端选择了接受 =="中间人服务器" 伪造的不可信证书==**;
> 2. **客户端主机由于中毒、安装了恶意软件等原因**,**被恶意导入了 "==伪造的根证书=="**,从而能够完成认证。
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-07EBFC187C4116E67ED9064D3FB4AFA2.png|496]]
>
>
> ![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-03CE6D4470F1084095E0BD8448040704.png|502]]
>
>
<br><br><br>
# HTTP 报文
HTTP 报文主要分为两种类型:
- **请求报文**(由客户端发送至服务器以发起请求)
- **响应报文**(服务器返回给客户端)

<br>
## HTTP 请求报文格式
HTTP 请求报文包括三个部分:**请求行、消息头(请求头)、消息体(请求体)**
- **请求行(Request Line)**,包括三个字段:
- **==请求方法==**:表明客户端对资源想要执行的操作,包括 `GET`、`POST`、`HEAD`、`PUT`、`DELETE`等。
- **==URL==**:指定请求的资源。若不是访问特定资源而是**对服务器本身发起请求**,可用 `*` 作为 URL。
- **==HTTP 版本==**:指明报文使用的 HTTP 版本,例如 `HTTP/1.1`
- **消息头 | 请求头(Header Line)**,由一系列 **键值对** 构成,提供了关于请求和客户端的额外信息,
- **空行**:请求头和请求体之间的一个空行,表示请求头部分的结束。
- **消息体 | 请求体(Entity body)**
- 发送给服务器的数据。通常只有 **POST 请求**才会携带请求体,**请求体的长度**由请求头 `Content-length` 给出。
> [!NOTE] "**请求行**" 以及 "**每一行请求头**" 都以 `\r\n` 作为结尾,空行则只包含 `\r\n` 。
<br>
### 请求头
常用的请求头包括:
- `Host`:服务器的域名或 IP 地址
- `User-Agent`:发出请求的浏览器或客户端的身份标识信息
- `Authorization`:提供**访问资源所需的认证凭证**(例如 JWT 令牌),即**用于验证用户身份的令牌**
- `Accept`:客户端**期望接收的响应数据类型**
- `Content-Type`:**请求体中的数据类型**(仅`POST` 和 `PUT` 请求有该字段),例如:
- 文本文件: `text/html` ,`text/plain`, `text/css`, `application/json` , `application/xml`
- 图片文件:`image/jpeg`,`image/gif`, `image/png`
- 视频文件:`video/mpeg`,`video/quicktime`
- 应用程序使用的二进制文件:`application/zip`
- `Content-Length`:请求体的长度(字节)
- 通常,仅 POST 方法具有该字段。
- `Connection`:连接控制选项,有两个作用:
- (1)指示**连接持久性**,可选值为 `keep-alive`,`close` 。
- `keep-alive`: 使用**长连接**,即保持 TCP 连接活跃,不中断。
- `close` :使用**短连接**,服务器在回发响应报文后,**主动关闭该 TCP 连接**。
- (2)控制**不再转发给代理的首部字段**,例如`Connection: Upgrade`。
- 
- `Cookie`:**提供存储在客户端的 Cookie 数据**
- `If-modified-since`:提供一个指定日期(这一日期由先前的服务器响应中的 `Last-modified` 头部提供,被浏览器或代理服务器存储在本地),告诉服务器**仅当其请求的资源对象在指定日期之后被修改过才发送该对象**,否则返回 `304 Not Modified` 状态码,不包含资源内容。
- 包含该头部的 GET 方法称为 "**条件 GET**" :
- 
- `Range`:用于指定对请求资源的下载范围,以字节为单位。
- "范围请求" 通常用于断点续传,例如下载过程中网络中断的情况,从断点处恢复下载,如下所示:
- 
> [!NOTE] 部分 HTTP 头部字段允许**多个值**
>
> 例如:
>
> - `Set-Cookie`:该字段可重复出现多次,每个值对应一项 Coocie
> - `Accept`:指定客户端可接受的媒体类型,可包含多个值,以逗号分隔;
> - `Cache-Control`:可包含多个指令,每个指令是一个值,例如 `no-cache`,`max-age=3600` 等。
>
> ```HTTP
> Set-Cookie: cookie1=value1
> Set-Cookie: cookie2=value2
> Accept: text/html, application/json
> ```
>
>
>
<br>
### HTTP 请求报文示例
GET 请求报文示例:
```http
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0
```
POST 请求报文示例:向服务器提交 JSON 格式的数据
```http
POST /api/comments HTTP/1.1
Host: www.exampleforum.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36
Accept: application/json
Content-Type: application/json
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...
Content-Length: 81
Connection: keep-alive
Cookie: userID=12345; sessionToken=abcdef1234567890
{
"postId": 123,
"author": "user123",
"comment": "This is a sample comment posted to the forum."
}
```
<br>
## HTTP 响应报文格式
HTTP 响应报文包括三个部分:**状态行**、**消息头(响应头)**、**消息体(响应体)**
- **状态行**:包括三个字段
- **==HTTP 版本==**:例如 `HTTP/1.1`。
- **==状态码==**:表示请求处理结果的数字代码,例如 `200`(成功)、`404`(未找到)等。
- **==状态短语==**:状态码的简短描述,例如 `OK` 或 `Not Found`。
- **响应头**:类似于请求头,包含了一系列键值对,提供**关于服务器和响应的额外信息**。
- **空行**:响应头和响应体之间的一个空行,表示响应头部分的结束。
- **响应体**:包含服务器返回的数据。这些数据可能是请求的网页、图片或其他类型的数据。
### 响应头
- `Connection`:连接控制选项,同请求头;
- `Server`:提供服务器的服务器软件信息,例如 `Apache/2.4.1 (Unix)`。
- `Date`:响应生成的日期时间
- `Content-Type`:响应体中的数据类型。
- `Content-Length`:响应体的长度(字节)
- `Set-Cookie`:**服务器向客户端设置的 Cookie**。
- 服务器能够**一次性向客户端浏览器发送多个 Cookie**,表现为**该项在响应报文中出现多行**,每个 `Set-Cookie` 行代表一个独立的 Cookie。
- `Last-Modified`:表示**资源最后一次被修改的日期和时间**。主要用于**缓存控制**和**条件性请求**的处理:
- (1)缓存控制:
- **协助缓存验证**:**浏览器或代理服务器可以存储资源的副本,并记录其 `Last-Modified` 时间**。在后续请求中,浏览器可以使用这个日期来判断资源是否已经被修改。
- **减少数据传输**:如果服务器上的资源自上次客户端获取以来未发生变化,则可以返回 304 Not Modified 状态码,而不是重新发送整个资源。这样可以节省带宽并加快加载速度。
- (2)条件性请求:
- **配合 `If-Modified-Since` 头**:客户端可以在请求头中包含 `If-Modified-Since` 字段,并设置为先前从服务器响应中收到的 `Last-Modified` 时间。服务器会检查资源自那时起是否被修改:
- 如果资源未修改,则返回 `304 Not Modified` 状态码,不发送资源的内容。
- 如果资源已修改,则返回新的资源内容和 200 OK 状态码。
#### HTTP 响应报文示例
```http
HTTP/1.1 200 OK
Date: Wed, 21 Oct 2023 07:28:00 GMT
Last-Modified: Wed, 21 Oct 2023 07:28:00 GMT
Server: Apache/2.4.1 (Unix)
Content-Type: application/json
Content-Length: 197
Connection: close
Set-Cookie: UserID=JohnDoe; Max-Age=3600; Version=1
Set-Cookie: SessionToken=abc123; Path=/; Secure; HttpOnly
{
"userId": "JohnDoe",
"name": "John Doe",
"email": "
[email protected]",
"roles": ["user", "admin"],
"profile": {
"age": 30,
"country": "USA"
}
}
```
<br><br><br>
# HTTP 请求方法
HTTP 协议中定义了一系列**请求方法**,表明对资源执行的操作。
常见方法包括 `GET`、`POST`、`PUT`、`HEAD`、`DELETE` 、`PATCH`、 `CONNECT`、`OPTIONS`、`TRACE` 等。
> [!NOTE]
> 这些方法的使用取决于所执行的操作和特定的应用场景。例如,**Web 浏览器主要使用 GET 来请求页面和 POST 来提交表单数据**。而**在构建 RESTful Web 服务时,开发者可能会使用全部或大部分这些方法**。
>
#### `GET` 方法
GET 方法用于向**服务器请求资源**(URL 资源),请求报文中 **==不包含消息体==**,携带的数据**包含在 URL 的查询字段**中,预期服务器能返回**完整的响应数据**(包含响应体),通常不改变服务器状态。
#### `POST` 方法
POST 方法用于**提交数据到服务器**,常用于**提交表单或上传文件**,**==数据包含在请求体中==**,可能改变服务器状态,例如在数据库中创建新数据。
#### `PUT` 方法
PUT 方法用于更新或创建指定的资源,**==数据包含在请求体==** 中。
PUT 与 POST 类似,但**通常用于更新替换现有资源或创建预定义 URI 资源**,例如**传输文件**。

#### `HEAD` 方法
与 GET 类似,HEAD 方法 **==没有请求体==**,且==**服务器在响应报文中只返回头部信息,不返回实际数据,无响应体**==。
HEAD 方法通常用于获取资源的元数据,如检查资源是否存在,或获取资源的最后修改日期。
#### `DELETE` 方法
用于**请求服务器删除资源**,一般不在请求体中发送数据。

#### `PATCH` 方法
用于对资源应用部分修改。
#### `CONNECT` 方法
用于建立特殊的连接隧道,通常用于 HTTPS 通过 HTTP 代理。
#### `OPTIONS` 方法
用于查询针对请求 URI 指定的资源支持的方法。

#### `TRACE` 方法
执行一个消息环回测试,让 Web 服务器端将之前的请求通信环回给客户端,主要用于调试,追踪路径。
<br>
### 关于表单提交
HTTP 中**提交表单的常见请求方式包括两种:`GET` 和 `POST`。**
这两种方法在表单数据的发送方式和使用场景上,通常 "**简单查询和非敏感数据可使用 GET,而敏感信息或大量数据应使用 POST"** :
- **使用 `GET` 方法提交表单**
- 说明:**表单数据附加在 ==URL 中作为查询字符串==**。数据以键值对的形式附加在 URL 后,各对之间用`&`分隔,例如:`?name=value&anothername=othervalue`。
- 适用场景:适用于**不涉及敏感数据且数据量较小**的查询(数据在 URL 中可见,因此不适用于涉密信息,如密码。
- 限制:**URL 长度有限,因此 GET 方法适用于少量数据**。大量数据可能导致 URL 过长,从而导致服务器无法处理请求。
- **使用 `POST` 方法提交表单**
- 说明:**表单数据包含在==请求体==中**,不会显示在 URL 中。
- 允许发送更大量的数据,并且数据不会在浏览器历史记录、Web 服务器日志文件或 URL 中直接暴露。
- **POST 请求的响应通常不被缓存**。
- 适用场景:
- (1)适用于需要安全提交的情况,如用户认证或文件上传。
- (2)适用于大量数据的提交,POST 方法没有数据大小的限制。
### 关于安全性和幂等性
- **安全方法**(如 GET、HEAD)不应当产生副作用,即不改变服务器状态。
- **幂等方法**(如 GET、PUT、DELETE、HEAD)意味着**无论执行多少次,结果都应该相同**。
<br><br><br>
# HTTP 状态码
> 参见 [^1] [^2]
HTTP 响应状态码是**服务器端用于表示 ==HTTP 请求处理结果==的三位数字代码**,可分为五个类别:
1. **消息响应**(`1xx`)
2. **成功响应**(`2XX`)
3. **重定向**(`3xx`)
4. **客户端错误**(`4xx`)
5. **服务器错误**(`5xx`)
![[_attachment/02-开发笔记/07-计算机网络/应用层/HTTP 协议.assets/IMG-HTTP 协议-05567D964719CB7B6DA2FCE8A65BAC51.png|623]]
### 消息响应(`1xx`)
- `100 Continue` : 目前为止一切正常,**客户端应继续请求**,如果已完成则忽略。
- `101 Switching Protocols` :响应客户端的 `Upgrade` 请求头,指明服务器即将切换的协议。
### **成功响应**(`2XX`)
- `200 OK` : 请求已成功,信息包含在返回的响应报文中(默认情况下,200 响应是可缓存的)
- 成功的意义取决于 HTTP 请求方法:
- `GET`: 资源已经获取并包含在消息体中;
- `HEAD`: 表示头部信息包含在响应报文中(不含消息体);
- `POST`: 描述操作结果的资源包含在消息体中;
- `TRACE`: 消息体中包含服务器接收到的请求消息;
> [!NOTE] `PUT` 或 `DELETE` 的成功结果通常不是 200 OK,而是 204 No Content (或者在第一次上传资源时创建的 201)。
- `201 Created`:请求已成功,并且**创建了新资源**。
- 这通常是在 POST 请求,或是某些 PUT 请求之后返回的响应。
- `202 Accepted`:请求已被收到,但服务器端的处理尚未完成(或者尚未开始),无返回内容
- 202 表示对于请求的处理是无保证的,可能会被执行也可能不会,服务器并不能稍后向客户端发送一个异步请求来告知其请求的处理结果。
- `203 Non-Authoritative Information`: 服务器**已成功处理了请求**,但返回的响应体并不是原始服务器的 `200 OK` 报文的响应体,而是来自另一处,例如已被 "转换代理" 修改。
- `204 No Content`:服务器已**成功处理请求**,但 **==没有返回内容==**,响应报文中**不包含响应体**。
- 这表明**客户端浏览器应保持当前页面**,不应当被替换或导航至其它页面。
- 例如,在实现 wiki 站点的“保存并继续编辑”功能时,可以使用这种方法。在这种情况下,将使用 PUT 请求来保存页面,并且将发送 204 No Content 响应来指示编辑器不应被其他页面替换。
- `205 Reset Content`:服务器已成功处理请求,客户端应当**重置文档视图**(即清除表单的内容,重置画布状态或 UI)。
- `206 Partial COntent` : 服务器已成功处理请求,返回的响应体中**包含请求资源的==指定范围的数据==**。
- **当客户端发送 `Range` 范围标头以只请求资源的"一部分"时,将使用此响应代码**。
- 如果只有一个范围,则将整个响应的`Content-Type` 设置为文档类型,并提供 `Content-Range`。
- 如果返回多个范围,则 `Content-Type` 被设置为 `multipart/byterange`,每个片段覆盖一个范围,由 `Content-Range` 和 `Content-Type` 对其进行描述。
- 
### 重定向(`3xx`)
- `300 Multipile Choices`:请求可能有多个响应,用户代理(user-agent)或用户应选择其中一个。
- 由于没有标准化的方法来选择其中一个响应,所以这个响应代码很少被使用
- `301 Moved Permanently`:**永久移动**。
- 请求的资源已经被永久性移动到了新的 URL,响应报文中的`Location` 头部字段包含资源的新 URL 地址。浏览器将自动重定向到新 URL,且搜索引擎应当更新到该资源的链接。
> [!NOTE] 建议 301 状态码只被用于 GET 或 HEAD 方法的响应,而 POST 方法的响应应该用 308 状态码
- `302 Found`:**临时移动**。
- 请求的资源已被临时性移动到了新的 URL,响应报文中的`Location` 头部字段包含资源的新 URL 地址。
- 浏览器会自动重定向到新 URL,但搜索引擎不应当更新到该资源的链接(在 SEO 语境下,意味着`link-juice` 不会被发送到新的 URL)
> [!NOTE] 建议 302 状态码只被用于 GET 或 HEAD 方法的响应,而 POST 方法的响应应该用 307 状态码。
- `303 See Other`: **查看其它地址**。
- 该状态码表示重定向链接指向**另一个页面**,而非原始请求的资源,**==用在处理 PUT 或 POST 请求之后,明确要求客户端使用 GET 方法在重定向的 URL 处获取资源==**。
- 这一状态码通常用在处理 PUT 或 POST 请求之后,例如在论坛中注册账号或登录后显示的"跳转到首页",或者上传文件后**跳转到消息确认页面或者上传进度页面**。
- `304 Not Modified`:客户端所请求的资源未修改。
- 服务器返回此状态码时,表明**不需要重新传输客户端所请求的资源**。
- 这一响应报文中**不包含响应体,同时必须包含 `200 OK` 响应中存在的报头**,包括 `Cache-Control`, `Content-Location`, `Date`, `ETag`, `Expires`, and `Vary`。
- 客户端通常会缓存访问过的资源,并在请求中提供一个头部信息来指明**客户端期望只返回在指定日期之后发生了修改的资源**,如果客户端请求的资源未被修改,则服务器端返回该响应代码。
- `307 Temporary Redirect`:
- 请求的资源已被临时移动到 Location 标头给出的 URL。该状态码要求**客户端在重定向到新 URL 时必须保留原始请求方法**。
- 该请求类似于 `302` 和`303`,不同在于:
- `302` **不限制客户端在重定向时的请求方法**;
- `303` 明确要求客户端在**重定向时必须使用`GET` 方法**;
- `307` 明确要求客户端在**重定向时保留原始请求方法**;
- `308 Permanent Redirect`:请求的资源已被永久性移动到 Location 标头给出的 URL。
### 客户端错误(`4xx`)
- `400 Bad Request`:**客户端错误**,服务器无法或不会处理该请求。
- 例如,错误的请求语法、无效的请求消息帧或欺骗性的请求路由。
- `401 Unauthorized`:**请求缺少有效的身份验证凭证**
- 这一响应报文中包含 `WWW-Authenticate` 头部,其中说明了客户端应当如何在携带用户身份认证信息后再次请求资源。
- `403 Forbidden`:**服务器已理解客户端的请求,但拒绝执行**。
- 该状态码通常与应用逻辑相关,例如用户权限不足以访问资源时,响应该状态码
- `404 Not Found `:**==服务器无法找到所请求的资源==**
- 指向 404 页面的链接通常被称为坏链接或死链接(broken or dead links)。
- 该状态码只表示**资源缺失**,并不明确缺失是临时的还是永久性的。如果明确资源已被永久移除,应当使用 `410 Gone` 状态码
- `405 Method Not Allowed`:**服务器已收到请求,但目标资源不支持该请求方法**
- 这一响应报文中必须包含`Allow` 头部,指明目标资源所支持的访问方法。
- `406 Not Acceptable`: 服务器无法提供与客户端请求指定的可接受值相匹配的响应,并且服务器不提供默认表示。
- 当客户端请求中包含 "*Proactive content negotiation headers*",例如 `Accept`、`Accept-Encoding`、`Accept-Language` 头部,而服务器无法提供匹配的结果时,将返回该状态码。
- 在实际应用中,这个错误状态码很少被使用。
- `407 Proxy Authentication Required`:请求缺少有效的**代理服务器的身份验证凭证**。
- 与 401 类似,但请求者应当**使用代理进行授权**。
- 该响应报文中包含了的 `Proxy-Authenticate` 头,描述了有关如何正确授权的信息。
- `408 Request Timeout`: 请求超时,**服务器等待客户端发送的请求时间过长**,服务器将断开链接。
- 服务器应该在响应中带有 `Connection: close` 头,表明服务器已经决定关闭连接,不再继续等待。
- `409 Conflict`: 请求**与服务器中目标资源的当前状态冲突**
- 在响应 PUT 请求时最有可能发生冲突。例如,当上载的文件比服务器上的现有文件旧时,可能会得到一个 409 响应,从而导致版本控制冲突。
- `410 Gone`: **请求的资源在服务器上已被永久移除**
此外,还有很多其他状态码,参见 [^1]:

### 服务器错误(`5xx`)
- `500 Internal Server Error`: 服务器内部错误,无法完成请求
- `501 Not Implemented`: 服务器不支持请求的功能,无法完成请求
- 501 是当服务器不能识别请求方法,并且无法支持为任何资源处理该请求方法时的适当响应。
- 服务器需要支持的唯一方法 (因此不能返回 501) 是 GET 和 HEAD。
- `502 Bad Gateway`:**作为网关或代理工作的服务器执行请求时,从上游服务器收到了无效响应**。
- 502 错误不是用户侧/客户端侧可以修复的,而是需要 web 服务器或者代理 (proxy) 侧进行修复
- `503 Service Unavailable`:服务器**尚未准备好处理请求**
- **常见的原因是服务器停机进行维护或过载**。此响应应用于临时条件,如果可能的话,Retry-After HTTP 报头应包含恢复服务的估计时间。
- `504 GateWay Timeout`:**作为网关或代理工作的服务器执行请求时,未能及时从上游服务器获得完成请求所需的响应**。
### 重定向状态码 `301`、`302`、 `303 `、`307`、`308`的区别
- `301 `,`302` **==不强制==限定客户端在重定向时的请求方法**;
- `302 Found` 表明请求的资源临时移动到了新的 URI,而**客户端在进行重定向时,"可以"保留原始请求方法(如 GET、POST)**,但很多现代浏览器实际上会把 POST 方法改为 GET 方法,可能导致不一致的行为。
- `301 Move Permanently` 表示资源**永久性**移动,其余同上;
- `303 See Other` 明确要求客户端在**重定向时==必须使用`GET`== 方法**;
- `303 See Other` 表示**重定向链接指向另一个页面**,用于处理 POST 或 PUT 请求后的重定向,**==明确要求客户端用 GET 方法在重定向 URL 处获取资源==**;
- `307`、`308` 明确要求客户端在**重定向时==保留原始请求方法和请求体==**;
- `307 Temporary Redirect` 表示请求的资源临时位于不同的 URI 下,与 302 类似,但**强制客户端在重定向时保留原始请求方法**(如果原始请求是 POST,重定向的请求也必须使用 POST 方法),相比 302 提供了对请求方法一致性的保证。
- `308 Permanent Redirect` 表示资源**永久性**移动,其余同上;
> [!caution]
>
> - "**永久移动 301 / 临时移动302** " 只应当被用于 GET 和 HEAD 方法的响应;
> - POST 的响应应该使用 "**永久移动 308 / 临时移动307**",从而确保不改变原始请求方法和请求体。
### 备注
- 部分状态码已被弃用,例如:
- `102 Processing`、`305 Use Proxy`
<br><br><br>
%%
# Cookie
> Cookie 也称为 HTTP Cookie、Web Cookie、浏览器 Cookie。
Cookie 是**由 Web 服务器发送到用户浏览器客户端的==小型数据片段==**,由浏览器存储在用户本地设备上。
之后当用户再次访问同一个 Web 网站时,**浏览器发送的数据中必须携带该 Cookie**。
Cookie 有多种用途,主要作用如下:
- **会话管理**
Cookie 可以存储登录状态、购物车内容、游戏分数或其他在页面间保持一致的信息。
例如当登录一个电子商务网站时,Cookie 会记录用户的登录状态,当用户在其它标签页中访问该网站的其它页面时,依然保持登录状态,而不需要每个页面都重新登录。
> 假设 Alice 在购物网站上有一个帐户。她从网站主页登录她的帐户。当她登录时,网站的服务器会生成一个会话 Cookie 并将 Cookie 发送到 Alice 的浏览器。此 Cookie 告诉网站加载 Alice 的帐户内容,因此主页现在显示“欢迎,Alice”。
>
> 然后,Alice 点击了一个显示一条牛仔裤的产品页面。当 Alice 的 Web 浏览器向网站发送一个关于牛仔裤产品页面的 HTTP 请求时,它在请求中包含了 Alice 的会话 Cookie。因为网站有这个 Cookie,所以它能识别出用户是 Alice,当新页面加载时,她不需要再次登录。
- **个性化设置**
网站使用 Cookie 来记录用户的偏好设置,如主题、语言选择或布局设置,以个性化用户体验。
> 如果 Alice 退出了购物网站,她的用户名可以储存在一个 Cookie 中并发送到她的 Web 浏览器。下次她加载该网站时,Web 浏览器会将这个 Cookie 发送给 Web 服务器,然后服务器会提示 Alice 用上次使用的用户名登录。
- **追踪和分析**
在线广告公司可能使用 Cookie 来跟踪用户在不同网站上的浏览活动,以提供定制化的广告内容。
> 如果 Alice 以前访问过一个向她的浏览器发送追踪 Cookie 的网站,这个 Cookie 可能会记录 Alice 现在正在浏览牛仔裤的产品页面。当 Alice 再次加载使用此追踪服务的网站时,她可能会看到牛仔裤的广告。
Cookie 使用示例:
> 
### Cookie 的类型
两种基本类型的 Cookie:
- **==会话 Cookie==**(Session Cookies)
会话 Cookie **仅在浏览器会话期间存在,关闭浏览器后会被删除**。通常用于临时存储。
- **==持久 Cookie==**(Persistent Cookies)
持久 Cookie**存储在用户本地硬盘上,即使浏览器关闭也会保留,直到达到设定的过期时间后才会被自动删除(也可被用户手动清理)。**用于长期存储用户偏好。
**根据用途划分**的 Cookie 类型:
- **==身份验证 Cookie==**
当用户通过浏览器登录帐户时,就会生成身份验证 Cookie。它们通过将用户帐户信息与 Cookie 标识符字符串联系起来,确保敏感信息被传递给正确的用户会话。
- ==**第三方 Cookie**==(Third-party Cookies)
第三方 Cookie 由非当前访问网站的域创建(即与用户当前浏览的页面不同的网站生成),当一个网页包含来自第三方域的内容(如广告)时,这些域可能设置 Cookie。第三方 Cookie 使广告商或分析公司可以在包含其广告的任何网站上跟踪个人的浏览历史记录。用于跨站追踪和广告服务。
- ==**安全 Cookie**==(Secure Cookies)
这种类型的 Cookie 只有 HTTPS 网站可以设置安全 cookie,即带有加密数据的 cookie,仅在加密的 HTTPS 连接上发送,用于提高安全性,保护用户数据不被窃取。
> 大多数情况下,电子商务网站的结账或支付页面都有安全的 cookie,以促进更安全的交易。同样,出于安全原因,网上银行网站也需要使用安全 cookie。
- ==**HttpOnly Cookie**==
这种 Cookie 不能被 JavaScript 访问,只能由 Web 服务器访问,可增强安全性、防止跨站脚本工具(XSS)。
- ==**同站 Cookie(SameSite Cookies)**==
这种 Cookie 用于控制 Cookie 是否应发送到跨站请求,用于提高隐私和防止跨站请求伪造(CSRF)攻击。
- **==跨站追踪 Cookie(Cross-site Tracking Cookies)==**
用于追踪用户在多个网站上的活动,主要用于广告定位和用户行为分析。
注意:
由于 cookie 可以被用于追踪用户的浏览习惯和行为(尤其是**第三方 Cookie 和跨站追踪 Cookie**),可能引起隐私问题:
- 为保护用户隐私,许多浏览器提供了对 Cookie 的管理功能,如阻止第三方 Cookie、限制 Cookie 的使用等。
> Google 已宣布到 2024 年在 Chrome 中终止使用第三方 Cookie
- 一些地区(如欧盟)要求网站在使用 Cookie 之前获得用户的同意,并明确告知 Cookie 的使用目的
### 设置 Cookie
服务器通过在响应报文中携带 `Set-Cookie` 首部字段来设置 Cookie。
单个响应报文中可以携带多个 Cookie,每行使用一个 `Set-Cookie` 首部字段来设置一个 Cookie。
Set-Cookie 字段属性如下:

- **expires** 属性
指定浏览器可发送 Cookie 的有效期。
当省略 expires 属性时,其有效期仅限于维持浏览器会话(Session)时间段内,这通常限于浏览器应用程序被关闭之前。另外,一旦 Cookie 从服务器端发送至客户端,服务器端就不存在可以显式删除 Cookie 的方法。但可通过覆盖已过期的 Cookie,实现对客户端 Cookie 的实质性删除操作。
- **path** 属性
限制指定 Cookie 的发送范围的文件目录。不过另有办法可避开这项限制。
- **domain** 属性
通过 Cookie 的 domain 属性指定的域名可做到与结尾匹配一致。比如,当指定 example. com 后,除 example. com 以外, www.example.com 或 www2.example.com 等都可以发送 Cookie。
因此,除了针对具体指定的多个域名发送 Cookie 之外,不指定 domain 属性显得更安全。
- **secure** 属性
Cookie 的 secure 属性用于限制 Web 页面仅在 HTTPS 安全连接时,才可以发送 Cookie。
发送 Cookie 时,指定 secure 属性:`Set-Cookie: name=value; secure`
此后,仅当在 https://www.example.com/ (HTTPS)安全连接的情况下才会进行 Cookie 的回收。也就是说,即使域名相同, http://www.example.com/ (HTTP)也不会发生 Cookie 回收行为。当省略 secure 属性时,不论 HTTP 还是 HTTPS,都会对 Cookie 进行回收。
- **HttpOnly** 属性
Cookie 的 HttpOnly 属性是 Cookie 的扩展功能,它使 JavaScript 脚本无法获得 Cookie。其主要目的为防止跨站脚本攻击(Cross-site scripting,XSS)对 Cookie 的信息窃取。
发送指定 HttpOnly 属性的 Cookie :`Set-Cookie: name=value; HttpOnly`
通过上述设置,通常从 Web 页面内还可以对 Cookie 进行读取操作。但使用 JavaScript 的 document. cookie 就无法读取附加 HttpOnly 属性后的 Cookie 的内容了。因此,也就无法在 XSS 中利用 JavaScript 劫持 Cookie 了。
### 使用 Cookie
客户端在具有服务器给予的 Cookie 后,向服务器发送请求报文时,在报文中通过 `Cookie` 首部字段来使用 Cookie。
单个请求报文中可以携带多个 Cookie,每行使用一个 `Cookie` 首部字段来携带一个 Cookie。
示例:
```
Cookie: userID=12345; sessionToken=abcdef1234567890
```
### 查看 Cookie
%%
# 参考资料
# Footnotes
[^1]: https://http.cat/
[^3]: [Site Unreachable](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Status)