跳转至

HTTP

简介

HTTP 是超文本传输协议,也就是HyperText Transfer Protocol。HTTP 是一种无状态无连接的基于 TCP/IP 的用户层传输协议。

HTTP 最突出的优点是简单、灵活和易于扩展、应用广泛和跨平台。

  • 简单:HTTP 基本的报文格式就是 header + body,头部信息也是 key-value 简单文本的形式,易于理解。

  • 灵活:HTTP 协议里的各类请求方法、URI/URL、状态码、头字段等每个组成要求都没有被固定死,都允许开发人员自定义和扩充。同时 HTTP 由于是工作在应用层( OSI 第七层),则它下层可以随意变化,比如:

HTTPS 就是在 HTTP 与 TCP 层之间增加了 SSL/TLS 安全传输层、HTTP/1.1 和 HTTP/2.0 传输协议使用的是 TCP 协议,而到了 HTTP/3.0 传输协议改用了 UDP 协议。

  • 跨平台:互联网发展至今,HTTP 的应用范围非常的广泛,天然具有跨平台的优越性。

HTTP 的却点就是明文传输不安全,无状态难以实现一些需要记忆用户信息的操作。

  • 明文传输不安全:在后续的 HTTPS 解决了该问题,它使用了 TLS 加密,安全问题也迎刃而解。

  • 无状态:多数服务器都是依靠 Cookie 和 session 机制,额外记录一些信息来实现具有关联性的操作。

内容

HTTP 协议的报文格式如下:

请求报文:

Image title

响应报文:

Image title

请求行/响应行

标识了 HTTP 协议的版本,有 1.0,1.1,2,3 等。

如果是请求头,会标识 HTTP 请求的类型,有 GET,POST,PUT,DELETE 等,会有访问的资源路径,这里是 \。还有 HTTP 协议的版本,有 1.0、1.1、2、3 等。

  • 请求类型:

GET:向特定的资源发出请求。

POST:向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST请求可能会导致新的资源的创建和/或已有资源的修改。

PUT:向指定资源位置上传其最新内容。

DELETE:请求服务器删除Request-URI所标识的资源。

如果是响应头,会指名 HTTP 协议版本和状态码。

  • 状态码:

状态码是服务器对我们该次请求处理的结果状态,状态码分为 5 类:

状态码 类别 原因
1xx 信息响应 接收的请求正在处理
2xx 成功响应 请求正常处理完毕
3xx 重定向 需要进行附加操作以完成请求
4xx 客户端错误 服务器无法处理请求
5xx 服务器错误 服务器处理请求出错

最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)。

这里的状态码,和 HTTP 的无状态无关。HTTP 的无状态意味着每个请求都是独立的,与之前的请求没有任何关联,服务器不会保留任何客户端请求的状态信息,这也就是说我们重复的向服务器申请同一资源时,服务器并不会识别出,我们曾多次申请过该数据。

但是浏览器可以能会对历史资源进行缓存,对针对缓存过的资源,并不会真的向服务器发送请求。

有一点对不上的是,我们在一个网站第一次登录之后,后续访问网站就不需要再次登录,这是因为使用了 Cookie,在后文介绍。

Headers

HTTP 报文的头部是一系列的 Key-Value 的键值对。

Host

客户端发送请求时,用来指定服务器的域名。

Connection

和服务器的连接方式,有长连接和短连接。HTTP 长连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态,便于其他请求复用。而短连接是在接收完数据后,就断开 TCP 连接。

这里的连接,也和 HTTP 的无连接无关,HTTP 的无连接是每个请求/响应对在完成之后,客户端与服务器之间的连接就会关闭。每次请求时都需要重新建立连接,这样的做法使得服务器无需维持大量持久连接,减少了服务器资源的消耗。

Location

搭配3xx状态码使用, 告诉客户端接下来要去哪里访问。

User-Agent

声明用户的操作系统和浏览器版本信息。

用于在客户端存储少量信息. 通常用于实现会话 (session) 的功能。

一般是当我们第一次登录一个网站时,服务器的响应 HTTP 报文中汇包含一个 set-cookie 字段,来让本地浏览器缓存一些用户信息,如密码,用户名等,当下次用户再次向该网站发送请求时,浏览器会自动将之前缓存的用户信息加到 HTTP 报文的 cookie 字段,来交给服务器进行验证,就可以避免用户多次重复的输入信息,以优化用户体验。

但是这个过程中有一个问题,就是服务器在进行 set-cookie 的响应中,用户信息是直接写在 HTTP 报文中的,这样的做法是很不安全的,用户的信息极易被盗取,这里就要提到 session,这时另一种记录客户状态的机制,不过 session 的用户信息被保存在服务器中,并且给每一个用户的信息分配一个独一无二 session ID 来标识每个用户,在服务器的响应 HTTP 报文中,也要进行 set-cookie ,不过这里让浏览器缓存的是 session ID,之后客户端再次访问时只需要将 session ID 交给服务器,即可辨别出用户。

但是这还没解决问题,只要抓包捕获到 session ID,就可以冒充用户。

数据相关

  • Accept:请求头内容,声明自己可以接受哪些数据格式。

  • Content-Type:响应头的内容,标识返回的数据类型,还有编码方式,如: text/html; Charset=utf-8

  • Accept-Encoding:请求头内容,说明自己可以接受哪些压缩方法。

  • Content-Encoding:响应头的内容,说明数据的压缩方法。表示服务器返回的数据使用了什么压缩格式。如:gzip。

  • Content-Length:响应头内容,标识数据长度。HTTP 协议通过设置回车符、换行符作为 HTTP header 的边界,通过 Content-Length 字段作为 HTTP body 的边界,这两个方式都是为了解决“粘包”的问题。

其他头部,后文再详细介绍。

Body

要传输的内容,可以没有,长度不限。

GET 和 POST 的区别

RFC 规定中 HTTP 协议中的安全是指请求方法不会修改服务器上的资源。

还有幂等的概念:多次执行相同的操作,结果都是相同的。

如果从 RFC 规范定义的语义来看:

GET 方法就是安全且幂等的,因为它是「只读」操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。

POST 方法是 不安全且不幂等的 ,因为是新增或提交数据的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。

但实际开发中,并不一定会遵循上述规范,具体的情况要看具体的实现。

在 RFC 的规范中并没有规定 GET 方法不能有请求体,而且 URL 中的查询参数并不是只能出现在 GET 请求中,POST 请求也可以。

缓存

对于一些重复的 HTTP 请求,效率更高的做法是把对应的响应数据在本地缓存一份,下次再请求该数据时,直接在读取本地的数据即可。在本地缓存就要处理如何与服务器同步的问题。

HTTP 采用强制缓存 + 协商缓存的做法。浏览器会在本地维护一份数据的有效时间,在有效时间范围内的,都直接使用本地缓存,当数据失效后,会向服务器发起一个协商缓存的请求,由服务器判断该本地数据是否失效,没有失效就返回状态码为 304 的响应,失效就返回新的数据。采用强制缓存获取到的数据的状态码与第一次获取到该响应时的状态码相同,并且会标识有 (from disk cache)。

强制缓存

强制缓存的实现使用了两个 HTTP 头部:

  • Cache-Control, 是一个相对时间,它提供了多种指令来控制缓存行为。以下是一些常见的指令:
public:响应可以被任何缓存存储。
private:响应仅针对单个用户,不能被共享缓存存储。
no-cache:强制每次请求直接到服务器验证响应。
no-store:禁止任何缓存存储请求或响应数据。
max-age=<seconds>:指定响应在缓存中可以存储的最大时间(以秒为单位)。
must-revalidate:在缓存过期后,强制要求缓存重新验证。

下面是一个使用 Cache-Control 头部字段的 HTTP 响应示例:

HTTP/1.1 200 OK
Content-Type: text/html
Cache-Control: public, max-age=3600

它标识了该响应可以被任何缓存存储,并且可以存储 3600 秒。

  • Expires,是一个绝对时间

下面是一个使用 Expires 字段的示例:

HTTP/1.1 200 OK
Content-Type: text/html
Expires: Wed, 21 Oct 2024 07:28:00 GMT

Expires 字段指定了资源的过期时间为 2024年10月21日 07:28:00 GMT。在此时间之前,浏览器和缓存服务器可以直接从缓存中读取该资源,而无需向服务器再次请求。

如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control 的优先级高于 Expires。

协商缓存

实现协商缓存有两种方式,一种是基于资源修改时间的,一种是基于资源标识符的。

基于资源修改时间的会使用到请求头部的 If-Modified-Since 字段和响应头部的 Last-Modified 字段。

  • If-Modified-Since:发送上次缓存的日期,询问服务器自该日期后资源是否修改。

  • Last-Modified:服务器返回资源的最后修改时间。

当本地的资源超过了强制缓存的有效时间,会向服务器发送一个带有 If-Modified-Since 字段的请求,服务器收到后,会比对该资源在服务器上的最后修改时间,如果服务器端的数据更新,就会将资源返回,否则就返回状态码 304 告知本地可以使用缓存。

但是这会有一些问题,可能系统时间会被修改,导致出现不可靠的问题,而且在没有修改文件内容的情况下,修改时间也可能改变,这会导致服务器重传数据。

基于资源标识符的会使用到请求头部的 If-None-Match 字段和响应头部的 ETag 字段。

  • If-None-Match:发送缓存资源的 ETag(实体标签),询问服务器资源是否改变。

  • ETag:服务器返回资源的唯一标识符。

当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的;

当本地的资源超过了强制缓存的有效时间,会在 Request 头部加上 If-None-Match 字段,该字段的值就是 ETag 标识;服务器再次收到请求后,会根据请求中的 If-None-Match 值与当前请求的资源生成的唯一标识进行比较:如果值相等,则返回 304 Not Modified,不会返回资源;如果不相等,则返回 200 状态码和资源,并在 Response 头部加上新的 ETag 标识;本地再更新 ETag 标识。

ETag 的优先级是高于 Last-Modified 的。

HTTP的演化

HTTP 1.1

HTTP 1.0 中每次请求都会建立一个 TCP 连接,效率低下。在 HTTP 1.1 中提出了长连接的概念,在一次请求后不立即关闭连接,知道有一方主动要求断开连接位置。

HTTP 1.0 是采用 请求-响应 的模式,即只有一次 请求 接收到 响应,才会发下一次的请求。如果某一次的响应很慢才回来,就会造成请求队列的阻塞。

HTTP 1.1 中提出了管线化(pipielining)机制,允许多个请求一起发出。但服务器还是按顺序接收和处理,返回的响应也是按顺序的。但如果一个响应资源特别大的话,要接收好久,后面小的资源都要等这个大资源接收完成,才可以被接受,就会导致响应队列的阻塞,降低用户体验。这就是队头阻塞(Head Of Line(HOL)blocking)问题,在 HTTP 2.0 中解决了问题。

HTTP 1.1 解决 HOL 阻塞问题的方法是打开多条 TCP 连接,并行的传输大资源和小资源,因为 TCP 拥塞控制的原因,每条 TCP 连接都要均匀的分配链路带宽,多条 TCP 连接意味着要占用更多的网络资源,这无疑增加了网络的负担(每条 TCP 连接都要经历握手回收的过程,采用 HTTPS 还要进行 TLS 握手)。

HTTP 2 的目标之一就是避免或减少传送单一Web 页面时的并行 TCP 连接。这样不仅可以减少服务器 socket 数量,也可以降低网络负担。

HTTP 2.0

  • 成帧

HTTP 2 解决 HOL 阻塞的方案是将报文分成多个大小相同的帧,并在一个 TCP 连接上交错发送不同响应报文的帧,这种方法就是并发的传输各报文,这样就不会因为一个大数据而阻塞其他数据了。

现在假设服务器要返回 9 个响应,1 个大的响应报文要被划分为 1000 帧,8 个小的响应报文分别要被划分为 2 帧。

HTTP 2的发送策略是:发送所有报文的第一帧后,再发送所有报文的第二帧,发送完所有报文的第二帧,再开始发送第三帧,依次类推,直到所有报文发送完成。

这意味着小报文在发送 18 帧后,就都被发送完成。不采用交错发送的话,发送完所有小报文就需要发送 1016 帧。

要将报文分成独立的帧,就意味着发送方和接收方要对数据进行划分和组装的过程,这个过程有 HTTP 2 的成帧子层来完成。当服务器要发送 HTTP 响应时,会先将处理好的 HTTP 报文交给成帧子层,由成帧子层将其划分为若干帧,响应首部为一帧,响应体会被划分为若干帧。划分完成后将数据交错的交给传输层的 TCP 协议。客户端收到帧后,会在成帧子层进行组装,只有完全组装完成后才会交给应用层用户使用。

成帧子层还会对数据进行二进制编码,对数据进行压缩,这样就可以减小数据体积,节约网络资源。

  • 响应报文的优先次序

HTTP 2 可以对请求添加相对优先级,权重为 1 到 256,HTTP 2会优先发送优先级最高的第一帧。这样当用户向服务器并发请求数据时,他能够为正在请求的响应确定优先次序。

  • 服务器推

HTTP 2 允许服务器为一个客户端请求,主动发送多个响应。HTTP 1.1 针对一个页面可能要想服务器请求,来获取 HTML,CSS,JS 资源,但是对于 HTTP 2,当用户发送一个请求后,服务器会将与对应 HTML 关联的 CSS,JS 数据一并发回客户端。这样避免了因等待这些请求而导致的额外时延。

HTTP 3.0

HTTP 3 不再使用 TCP 作为传输层协议,而是 UDP,更准确的说是一种基于 UDP 实现的传输层协议:QUIC 协议。它基于 UDP 实现了许多 TCP 特性。 QUIC 会在传输层篇章详细介绍。

HTTPS

HTTP 都是明文传递数据,数据都是不安全的。HTTPS 就是在 HTTP 的基础上加了一层 SSL\TLS 的加密层。

加密相关概念
  • 对称加密

采⽤单钥密码系统的加密⽅法,同⼀个密钥可以同时⽤作信息的加密和解密,这种加密⽅法称为对称加密,也称为单密钥加密,特征:加密和解密所⽤的密钥是相同的。

常⻅对称加密算法(了解):DES、3DES、AES、TDEA、Blowfish、RC2等

特点:算法公开、计算量⼩、加密速度快、加密效率⾼

对称加密其实就是通过同⼀个 "密钥" , 把明⽂加密成密⽂, 并且也能把密⽂解密成明⽂.

  • 非对称加密

需要两个密钥来进⾏加密和解密,这两个密钥是公开密钥(public key,简称公钥)和私有密钥(private key,简称私钥)。

常⻅⾮对称加密算法(了解):RSA,DSA,ECDSA

特点:算法强度复杂、安全性依赖于算法与密钥但是由于其算法复杂,⽽使得加密解密速度没有对称加密解密的速度快。

⾮对称加密要⽤到两个密钥, ⼀个叫做 "公钥", ⼀个叫做 "私钥",公钥和私钥是配对的,被公钥加密的数据只能由私钥解密,被私钥加密的内容只能被公钥解密。

  • 数据摘要

数据摘要(数据指纹),其基本原理是利⽤单向散列函数(Hash函数)对信息进⾏运算,⽣成⼀串固定⻓度的数字摘要。数字指纹并不是⼀种加密机制,但可以⽤来判断数据有没有被窜改。这可以理解为一种超损压缩。

摘要常⻅算法:有MD5、SHA1、SHA256、SHA512等,算法把⽆限的映射成有限,因此可能会有碰撞(两个不同的信息,算出的摘要相同,但是概率⾮常低)

摘要特征:和加密算法的区别是,摘要严格意义不是加密,因为没有解密,只不过从摘要很难反推原信息,通常⽤来进⾏数据对⽐。

  • 数字签名

就是对原数据进行依次散列计算,得到其数据指纹,再将数据指纹用签名者的私钥进行加密,加密得到的密文就是签名。

当用户端拿到带签名的数字后,只需拿公钥解密签名,对比签名和拿到的数据指纹是否相同,就可以知道数据是被被篡改过。

Image title

HTTPS 使用 非对称加密 + CA 证书的方式交换密钥,然后通过对称加密进行通讯。

CA 证书

CA 机构是一系列可信的权威机构,要使用 HTTPS 服务的企业或个人,要向 CA 提交以 .csr 为后缀的文件进行证书申请,该文件中要包含域名、申请者、企业信息和一个公钥(后面说公钥的作用),CA 机构会向提取出这些信息的数字指纹,并用机构内的密钥生成一个数字签名,与上述信息的明文打包在一起,形成一份证书,也就是说:证书 = 申请者信息 + CA 机构生成的数字签名

这样当用户要和服务器进行通信时,服务器会先将证书发个用户,用户会用其内置的 CA 机构的公钥区解密这个数字签名,来验证明文信息是否正确。确认无误后,用户会生成一个对称加密的密钥,用明文中的公钥加密,发给服务器,服务器中使用私钥,就可以获得用户提供的密钥,至此双方就可以使用改密钥进行安全通信了。

Image title

这个过程中涉及两队公钥和私钥,一对是 CA 机构用于数字签名的,密钥被 CA 机构持有,公钥大多在用户的设备中内置

查看 CA 机构证书

以 chrome 为例,可以在浏览器的设置 -> 隐私与安全 -> 安全 -> 管理证书中看到浏览器内置的证书。

Image title

证书链

根 CA 机构下有很多子 CA 机构,可能会划分很多层,每层 CA 机构都由上层 CA 机构颁发证书,根 CA 机构会自验证。

有些时候用户不会持有所有子 CA 机构的公钥。只要用户机器内由这些根 CA 机构,就可以通过证书链,来验证其信任的所有子 CA 机构。用户机器会一层一层的向每个 CA 机构请求证书,直到根 CA 机构,在通过上一层 CA 机构证书中的公钥来验证下一层 CA 机构的证书,最终完成网站证书的验证。

TLS RSA 握手

TLS ECDHE 握手


参考:

小林coding 图解网络

从点击网页到显示需要多少步-DNS、HTTP、CA、TLS、HTTPS