概述
HTTP(Hypertext Transfer Protocol,超文本传输协议)是计算机网络中应用层的一个通信协议,它依靠 TCP 协议进行可靠数据传输。
目前经历了 4 次版本迭代,分别是 0.9,1.0,1.1,2.0 和 3.0,现代浏览器多使用 1.0 版本。
HTTP 协议通信示意图如下:
通信双方分别是客户端和服务端,采用请求 - 响应模式。
- 客户端:客户代理,绝大数情况下是浏览器,用户通过浏览器发送请求。
- 服务端:一般是一个 Web 服务器,接收并处理客户端请求,返回处理结果。
HTTP/0.9
产生原因
HTTP/0.9 是初代版本,诞生于1991年,仅传输 HTML 文件,主要用于学术的交流。
特点
- 只支持 GET 方法。
- 只有请求行,没有请求头和请求体。比如:
GET /filepath
。 - 只有响应行,没有响应头和响应体。
- 响应以 ASCII 字符流的形式返回。
HTTP/1.0
产生原因
随着互联网的发展,人们需要与服务端交互,服务端需要传输脚本、样式、图片、音频和视频等非 HTML 文件。
因此 HTTP/1.0 的核心是支持多类型文件下载。
新增内容
- 增加请求头和请求体。
- 增加响应头和响应体。
- 增加 POST、HEAD 等新方法。
- 增加响应状态码,表示请求的处理情况。
- 增加缓存机制,缓存客户端下载过的资源。
……
缺点
- 非持续连接。一个 TCP 连接只处理一个请求,服务端发送响应后断开连接。
- 若出现大量请求,反复建立和断开连接的代价巨大。
- 若某一请求的 HTML 包含多个如图片、视频等链接,处理该请求就需要多个 TCP 连接。
- 请求阻塞问题。服务端只有一个域名,浏览器对单个域名建立 TCP 连接的数量有上限(一般为 6 个,不同浏览器可能有差异),到达上限后剩余请求将被阻塞。
- 队头阻塞问题。HTTP 1.0 规定在前一个请求响应到达之后下一个请求才能发送,如果前一个请求没得到响应,后面的请求就会被阻塞。
HTTP/1.1
产生原因
HTTP/1.0 中一个 TCP 连接只处理一个请求,资源开销巨大,因此 HTTP/1.0 的核心是提高性能。
新增内容
- 新增 PUT、DELETE 等方法。
- 默认采用 TCP 长连接。当请求的 HTML 包含多个资源时,都用同一个 TCP 连接进行传输。
- 采用 cookie 客户端维持状态。HTTP 是一个无状态协议,即服务端不记录请求的处理结果,之前被处理的请求不会对当前要处理的请求造成逻辑上的影响。
- 支持虚拟主机(Host 字段)。一个 IP 可以对应多个域名,借此服务器可以通过多个域名提供服务,解决了单个域名 TCP 连接数量上限的问题。
- 支持动态内容大小。HTTP/1.0 中通过 Content-Length 来描述传输内容的大小,在 HTTP/1.1 中通过 Chunked transfer encoding 机制将数据分块,每一块描述上一块的大小,最后通过 0 大小块来标志完成传输。
- 不成熟的管道化。HTTP/1.1 尝试一次建立多个 TCP 连接来发送请求,服务端按请求到来的顺序依次处理响应,若其中一个响应由于某种原因慢了一些,后面的所有响应都会被阻塞。
……
缺点
- 队头阻塞问题。
响应 2 由于某种原因延迟了,导致响应 3 阻塞。
HTTP/2.0
产生原因
针对 HTTP/1.1 的性能瓶颈进行优化。
- 队头阻塞问题。
- TCP 慢启动问题。TCP 协议具有拥塞控制功能,TCP 连接传输数据由慢到快,先指数增长再线性增长。
新增内容
- 二进制分帧和多路复用技术。
- 一个域名只用一个 TCP 长连接,避免 TCP 慢启动和拥塞控制。
- 二进制分帧:客户端将所有请求分成带标识的二进制帧,然后发送给服务端,服务端将同一个请求的帧组合成完整的请求后处理。
- 多路复用:不同的请求共用一条 TCP 连接发送,主要靠二进制帧中的标识区分。
SSL 或 TSL 是安全传输层,是可选的。
客户端负责将请求拆分为二进制帧然后发送,由服务器负责重组。
- 可设置请求优先级。采用多路复用后,可以设置请求优先级,当有优先级更高的请求重组完成时,服务端可以优先处理它。
- 采用头部压缩算法,减少数据传输。
- 支持服务器推送。当客户端请求一个 HTML 页面后,服务端把该页面内引用的其他资源一并推送给客户端。
缺点
队头阻塞问题。多路复用技术只在请求粒度(或 HTTP 层)上解决了该问题,在 TCP 报文粒度(或 TCP 层)上仍然存在。
因为 TCP 是一个可靠的、按序交付的传输协议,当发生丢包时需要重传,这将阻塞后续 TCP 报文的发送。
HTTP/3.0
产生原因
进一步优化 HTTP/2.0 的性能瓶颈。
归根到底,导致 HTTP/2.0 出现性能瓶颈的根本原因是采用了 TCP 协议进行传输,因此 HTTP/3.0 采用了 UDP 协议。而为了保证可靠运输,Google 在 UDP 层上加入了自研的 QUIC 层。
新增内容
- 类 TCP 的拥塞控制、流量控制、可靠传输等功能。
- 集成了 TLS 加密传输功能。
- 实现了 HTTP/2.0 中的多路复用技术。QUIC 实现了在同一物理连接上可以有多个独立的逻辑数据流。实现了数据流的单独传输,就解决了 TCP 中队头阻塞的问题。
HTTPS
为什么出现 HTTPS 协议
因为 HTTP 协议是明文传输的,不安全。
- 窃听风险:不法分子可以窃取 HTTP 传送的内容。
- 篡改风险:不法分子可以修改 HTTP 传送的内容。
- 冒充风险:不法分子可以冒充他人参与通信。
HTTPS 如何实现安全传输
没有什么是加一层不可以解决的!
通过引入 SSL 或 TSL 层,先对要传输的数据进行加密来保证安全。
SSL(Secure Socket Layer,安全套接字层)。
TLS(Transport Layer Security,传输层安全),其前身是 SSL。
加密算法
对称加密
因为加密秘钥和解密密钥实质上等同(从一个可以推出另一个),因此对称加密也叫单密钥加密,看作只有一个密钥,加密和解密都用相同的密钥和加密、解密算法。
当且仅当只有通信双方拥有密钥时,对称加密是安全的。
例子:DES、AES-GCM
问题
如何做到当且仅当只有通信双方(客户端和服务端)拥有密钥。
- 若客户端在发送请求时,顺便夹带密钥,但是第三方可以拦截下来,不安全。
- 若服务器响应请求时,顺便夹带密钥,相当于没加密,任何人请求都发密钥,不安全。
因此 HTTPS 不能只采用对称加密。
非对称加密
因为加密秘钥和解密密钥实质上不等同(从一个难以推出另一个),因此叫非对称加密。
公钥加密的内容只有私钥能解密,私钥加密的内容只有公钥能解开。
例子:RSA、DSA
问题
- 客户端给服务器发送用公钥加密的请求,只有服务器能解密,安全。
- 但是服务器给客户端发送的响应可以被任何人拦下来,用公钥解密,不安全。
因此非对称加密只能保证单向安全, HTTPS 不能只采用非对称加密。
改良的非对称加密
既然一组公钥和密钥只能保证单向安全,那么两组呢?
使用两组公钥和私钥,客户端和服务端各有一组(A1, A2 和 B1, B2,前公钥,后私钥)。
- 客户端请求时,顺便夹带公钥 A1 给服务端。
- 服务端响应时,顺便夹带公钥 B1 给客户端。
- 客户端用公钥 B1 加密请求,只有服务端有密钥 B2 能解密。
- 服务端用公钥 A1 加密响应,只有客户端有密钥 A2 能解密。
对称加密 + 非对称加密
- 客户端发起请求,服务端响应时顺便夹带公钥 A1 给客户端。
- 客户端随机生成对称加密的密钥 X,用得到的公钥 A1 加密密钥 X 传给服务端。
- 服务端用私钥 A2 解密得到密钥 X。
- 满足当且仅当只有客户端和服务端拥有密钥,可以安全通信了。
问题
中间人攻击:替换公钥。
中间人自己拥有公钥 B1 和私钥 B2。
- 客户端发起请求,服务端响应时顺便夹带公钥 A1 给客户端。
- 中间人拦截数据包获得公钥 A1,并把公钥 A1 替换为自己的公钥 B1,然后传给客户端。
- 客户端用公钥 B1 加密密钥 X 传回服务端。
- 中间人拦截数据包用私钥 B2 解密获得密钥 X,再用公钥 A1 加密传给服务端。
- 服务端用私钥 A2 解密获得密钥 X。
结果:中间人也拥有了密钥 X,可以拦截并解密数据包。
HTTPS 加密
在对称加密 + 非对称加密过程中,出现中间人攻击的原因是:客户端不知道公钥是否是服务端发送的。
HTTPS 通过引入一个 CA 机构来解决这个问题。
证书颁发机构(CA, Certificate Authority)即颁发数字证书的机构。是负责发放和管理数字证书的权威机构,并作为电子商务交易中受信任的第三方,承担公钥体系中公钥的合法性检验的责任。
证书的组成
- 持有者信息。
- 公钥。由 CA 机构生成。
- 数字签名。数字签名由 CA 机构颁发前,用哈希算法和私钥加密所得,过程如下:
明文来自证书内容。
……
加密过程
关键:证书防伪?
CA 证书验证
包括很多方面,主要有数字签名验证等。
数组签名验证过程如下:
证书防篡改
若中间人拦截证书获取公钥,由于中间人没有私钥,所以最多篡改原文,不能篡改签名(可以随便替换一个无意义的签名,但无济于事),再发回给客户端。
- 若篡改了公钥,则客户端连数字签名都不能解密(或解密得到的结果一定不正确)
- 若篡改了其他内容,哈希所的值发生变化。
任何篡改方式都将导致值 1 与值 2 不相等,验证失败,证书无效,重发请求。
证书防调包
中间人从 CA 机构获得其他客户端的证书 B,从拦截证书 A 并用证书 B 替换,再发送给客户端,客户端就收到了证书 B。
这样客户端能通过数字签名验证,似乎有漏洞?
并不是,CA 证书验证不只是数字签名验证,还有域名验证(证书有持有者信息),客户端发现自己请求的域名和证书的不一致(因为这个证书来自其他服务器,并不是客户端请求的服务器),验证失败。
为什么需要多算一次哈希值
若直接对明文 T 加密得到数字签名,客户端解密得到明文 T 进行对比,即可防篡改、掉包。
那为什么要多算一次哈希值呢?
可能考虑性能问题,明文一般较长,加密耗时,通过 MD5 散列算法可以得到固定长度为 32 字节的散列值。