浏览器 - TCP 协议与 HTTP 请求

TCP 协议和 HTTP 请求的基础知识,HTTPS 的工作模式

TCP 的三次握手

TCP 建立连接的时候需要“三次握手”:

第一次握手:建立连接时, 客户端发送 syn 包到服务器,等待服务器确认;

第二次握手:服务器收到 syn 包,确认客户端的 SYN,同时自己也发送一个 SYN+ACK 包给客户端;

第三次握手:客户端接收到 SYN+ACK 包,向服务端发送 ACK 包,此包发送完毕,TCP 链接成功;

三次握手的目的是:为了防止已经失效的连接请求报文段突然又传到服务端,因而产生错误。如果采用二次握手,因为 A 端到 B 端的请求可能在某个网络节点上发生滞留,过了一段时间后才到达 B 端,对于 A 来说这已经是一个失效的报文,但 B 会误认为这是一个新的报文而与 A 建立连接。A 不会再通过这个连接发送数据,B 没有收到数据会一直等待,导致资源的浪费。

TCP 的四次挥手

TCP 在关闭连接的时候需要四次挥手:

  1. 客户端发送一个 FIN 给服务端用于关闭 TCP 连接;
  2. 服务端接收 FIN 之后发送一个 ACK 给客户端;
  3. 服务端先关闭客户端的连接,然后发送一个 FIN 给服务端;
  4. 客户端发回 ACK 确认;

为什么 B 还需要告诉 A 要关闭连接呢?因为 A 想关闭连接时 B 可能还没有传输完数据,所以连接还不能被真正地关闭(此时处于半关闭状态)。

HTTP 协议

HTTP 协议是一种基于TCP/IP通信协议来传输数据的一种无状态协议,无状态协议不要求服务器在多个请求期间保留有关每个用户的信息或状态。

HTTP/1.1

HTTP/1.1 标准解决了早期版本中发现的许多协议歧义,并引入了许多关键性能优化:keep-alive 连接,分块编码传输,字节范围请求,附加缓存机制,传输编码和管道式请求。

  • keep-alive 连接:

    允许复用现有的 TCP 连接,以便于处理同一主机发出多个请求(除非特别声明头部Connection: close,否则默认使用 keep-alive)。

    开启了 keep-alive 时,请求结束,TCP 连接不断开,继续保持一段时间,在这段时间内,同一客户端向服务器发送请求都会复用该 TCP 连接并重置 timeout 时间计数器,在接下来 timeout 时间段内还可以继续复用 TCP 连接。

    但是他也有一个缺点:每次请求必须等待上一次响应之后才能发起;

  • 分块编码传输:允许将服务端到客户端的数据分成多个部分进行传输;

  • 字节范围请求:请求头的 Range 属性,可以获取指定范围字节的数据;

  • 附加缓存机制:强缓存和协商缓存;

  • 传输编码:传输编码是为了分块传输而设计的,能够改变报文的格式和传输的方式;

  • 管道式请求:相对于 keep-alive 连接的又一性能优化,可以将多条请求放入队列,无需等待服务器对前一个请求进行相应。但默认不开启,因为这要求服务端必须按照请求发送的顺序进行响应,当顺序请求多个文件时,其中某一个请求因为某种原因被阻塞时,后面排队的所有请求也一并被阻塞,这就是队头阻塞。

HTTP/2.0

HTTP/2 的主要关注点是提高传输性能并实现更低的延迟和更高的吞吐量。

那么 HTTP/2.0 多路复用和 HTTP/1.1 keep-alive 有什么区别?

  • HTTP/1.1 是基于文本的,只能整体去传递;而 HTTP/2 是基于二进制流的,可以分解为独立的帧,交错发送;
  • HTTP/1.1 keep-alive 单个 TCP 连接在同一时刻只能处理一个请求,而 HTTP/2.0 单个 TCP 连接能够在同一时刻处理多个请求和响应;
  • HTTP/1.1 keep-alive 必须按照请求发送的顺序返回响应,而 HTTP/2.0 不需要;
  • HTTP/1.1 keep-alive 为了解决队头阻塞,需要将同一个页面的资源分散在不同域名下,开启多个 TCP 连接,而 HTTP/2.0 所有通信都可以在单个连接上完成;

HTTP 请求

当我们在浏览器里输入一个域名并访问的时候,浏览器会将该域名发送给 DNS 服务器,服务器将其解析为 IP 地址,浏览器与 IP 地址所对应的服务器建立 TCP 连接,然后才发送 HTTP 请求。

HTTP 请求可以分为 3 个部分:请求行、请求头、请求体

请求行

请求行包含了方法、URL 和 HTTP 版本信息,方法是指请求的类型:

  • GET:从服务器取回数据
  • POST:上传文件或者表单
  • PUT:更新已有实体
  • DELETE:删除资源
  • TRACE:返回请求发生时,服务端收到的内容
  • HEAD:仅返回头部,不返回响应体
  • OPTIONS:返回服务端支持的请求方法
  • CONNECT:用来建立一个对资源的网络连接

请求头

请求头内包含着 Accept-Charset(客户端可接受的字符集)、Content-Type(正文格式)、Cache-Control 等字段。

HTTPS 请求

HTTPS 请求是结合了对称加密和非对称加密的 HTTP 请求

对称加密

对称加密:客户端和服务端存有相同的秘钥,使用秘钥产生的密文可以在任意端解密。这是效率很高的加密方式,但是在服务端发送秘钥给客户端的过程中,秘钥很容易被截获。

非对称加密

非对称加密:服务端保存私钥,客户端保存公钥,私钥生成的密文可以使用公钥解密,公钥生成的密文可以使用私钥解密。服务端将公钥发送给客户端,客户端生成的密文没有服务端的私钥是解不开的,解决了对称加密的安全性问题。不过只使用一对公私钥加密是不够的,因为公钥是公开的,所以任何人都可以模拟发送请求,任何人都可以解密服务端发出的信息,所以客户端也需要配置自己的公私钥,并且将客户端的公钥发送给服务端,这样即使请求被拦截,请求里面的密文也不会被他人解密。

中间人攻击

HTTPS 使用了非对称加密+对称加密的形式,但在非对称加密阶段可能遭到中间人攻击。

如果中间人劫持了服务器发送给客户端的公钥明文 M,并将中间人的虚假公钥 X 传递给客户端,对于客户端来说并不知道公钥 M 已经被替换了,会使用虚假公钥 X 生成对称加密的密钥 D。

此时中间人就可以通过自己的私钥 XX 去解密请求里用虚假公钥 X 加密的信息,然后再用公钥 M 对信息进行包装,发送给服务端,整个过程无法被发现。

出现中间人攻击的原因就是浏览器无法判断公钥是否来自正确的服务端,此时就需要用到数字证书,用来证明当前公钥的身份。

数字证书

数字证书的生成,简单的来说,就是:将明文的哈希值用私钥加密,然后将明文和加密后的哈希值打包发送给客户端,在客户端用公钥解密,并重新计算明文的哈希值,如果两个哈希值一致,说明没有被篡改。

详细步骤:

如果将所有者的公钥、证书所有者、发布机构和有效期等信息称作明文M, CA 会先对明文 M 进行 hash 运算产生文件 H,然后使用 CA 的私钥对文件 H 进行加密,得到签名 Q,然后将明文 M 和签名 Q 合并成一个文件,就获得了数字证书。

客户端在获得数字证书之后,会通过 CA 的公钥对签名 Q 进行解密,得到 hash 值,然后对明文 M 进行 hash 运算,如果解密获得的 hash 值和运算生成的 hash 值一致的话,那么说明这个证书是未被篡改的。

Tips:

浏览器和操作系统内一般内置了权威的 CA 机构的数字证书,用于获取到正确的 CA 公钥。

如果在根证书中加入数字证书K,就能让浏览器信任该证书,就可以实施中间人攻击了,charles就是使用这种模式来抓包 Https 的。

HTTPS 的工作模式

下图反应的是 HTTPS 的工作模式,

  1. 客户端首先生成一个随机数,然后将【加密算法列表、客户端生成的随机数】发送给服务端。

  2. 服务端将客户端发送过来的随机数存储下来,然后自己生成一个随机数,并在加密算法列表中选择一个算法,将【选择的算法、服务端生成的随机数、服务端证书】发送给客户端,最后告诉客户端:信息发送完成。

  3. 客户端对服务端发送过来的证书进行校验,获得服务端的公钥,然后生成一个【对称加密用的随机数字】,使用公钥进行加密,发送给服务端。

  4. 服务端接收到客户端发送过来的密文,使用本地私钥进行解密,算出【对称加密用的随机数字】。

  5. 现在双方都存在:客户端的随机数、服务端的随机数、对称加密用的随机数,使用这几个参数生成对称加密的秘钥,那么现在就可以尝试使用对称加密进行传输了。

HTTPS是否需要每次请求都生成新的密钥?

服务器会为每个浏览器(或客户端软件)维护一个session ID,在TSL握手阶段传给浏览器,浏览器生成好密钥传给服务器后,服务器会把该密钥存到相应的session ID下,之后浏览器每次请求都会携带session ID,服务器会根据session ID找到相应的密钥并进行解密加密操作,这样就不必要每次重新制作、传输密钥了。

附录

常用状态码

200 :ok,请求成功

201:created,成功请求并创建了新的资源

202:accepted,已接受请求但未处理完成

301:永久重定向

302:临时重定向

304:not modified,所请求的资源未修改,用于协商缓存

400:bad request,客户端请求语法错误

401:unauthorized,请求要求用户身份认证

403:forbidden,服务器理解请求客户端的请求,但是拒绝执行此请求

404:not found,无法找到请求的资源

500:internal server error,服务器内部错误

501:not implemented,服务器不支持请求的功能,无法完成请求

502:bad gateway,从远程服务器接收到了一个无效的响应

503:service unavailable,由于超载或者系统维护,服务器暂时无法处理客户端的请求

浏览器 - TCP 协议与 HTTP 请求

https://hashencode.github.io/post/997c7fc1/

作者

BiteByte

发布于

2020-08-08

更新于

2024-11-15

许可协议