跳转至

🟡 HTTP & HTTPs

约 9488 个字 8 行代码 3 张图片 预计阅读时间 48 分钟

HTTP 状态码的含义?

100 类

100 类状态码属于提示信息,是协议处理中的一种中间状态,实际用到的比较少

  • 100(继续):请求者应当继续提出请求。 服务器返回此代码表示已收到请求的第一部分,正在等待其余部分
  • 101(切换协议):请求者已要求服务器切换协议,服务器已确认并准备切换

200 类

200 类状态码表示服务器成功处理了客户端的请求。

  • 200(成功):表示服务器响应成功,也就是服务器找到了客户端请求的内容,并且将内容返回给客户端
  • 204(已创建):请求成功并且服务器创建了新的资源
  • 206(部分内容):服务器成功处理了部分 GET 请求

300 类

300 类状态码表示客户端请求的资源发生了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向。

  • 301(永久移动):代表 永久性的重定向,值得注意的是,这种重定向跳转,从严格意义来讲不是服务器跳转,而是 客户端跳转 的。这个“跳”的动作是服务器是通过回传状态码 301 来下达给客户端的,让客户端完成跳转
  • 302(临时移动):代表临时跳转。例如:URL 地址 A 可以向 URL 地址 B 上跳转,但这并不是永久性的,在经过一段时间后,URL 地址 A 还可能向 URL 地址 C 上跳转
  • 304(未修改):服务器通过返回状态码304可以告诉客户端请求资源成功,但是这个资源不是由服务器提供返回给客户端的,而是客户端本地浏览器缓存中就有的这个资源,因为可以从缓存中获取这个资源,从而节省传输的开销

400 类

400 类状态码表示客户端发送的报文有误,服务器无法处理。

  • 400(错误请求):服务器不理解请求的语法
  • 401(未授权):表示客户端请求需要身份验证,客户端需要在请求头中提供有效的身份认证信息
  • 403(禁止):代表请求的服务器资源权限不够,也就是没有权限去访问服务器的资源,或者请求的 IP 地址被封掉了
  • 404(未找到):代表服务器上没有该资源,或者说服务器找不到客户端请求的资源,是最常见的请求错误码

500 类

500 类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。

  • 500(服务器内部错误):代表程序错误,也就是说请求的网页程序本身报错了。在服务器端的网页程序出错。由于现在的浏览器都会对状态码 500 做一定的处理,所以在一般情况下会返回一个定制的错误页面。
  • 501(尚未实施):服务器不具备完成请求的功能。 例如,服务器无法识别请求方法时可能会返回此代码。
  • 502(网关错误):通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误。
  • 503(服务不可用):表示服务器当前很忙,暂时无法响应客户端。
  • 504(网关超时):服务器作为网关或代理,但是没有及时从上游服务器收到请求。
  • 505(HTTP 版本不受支持):服务器不支持请求中所用的 HTTP 协议版本

HTTP 常见字段

  • Host:指定服务器域名
  • Content-Length:为了解决 TCP 粘包问题所设计的 HTTP 数据长度
  • Connection:是否使用长连接
  • Content-Type:指定数据格式,例如文本或图像、编码方式
  • Content-Encoding:数据的压缩方法,如 gzip 等等

HTTP 缓存有哪些实现方式?

  • 强制缓存:强制缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边。相关字段有 Cache-ControlExpires,前者优先级更高
  • 协商缓存:请求的响应码 304,告诉浏览器可以使用本地缓存的资源,通过服务端告知客户端是否可以使用缓存的方式被称为协商缓存。请求头部用 If-Modified-Since;响应头部使用 Last-Modified 来维护。当发现资源过期时请求方会带上 Last-Modified 字段,如果修改过则返回 200 OK 以及相关资源,否则继续 304 走缓存
  • 协商缓存还可以通过 If-None-MatchETag 两种字段实现。前者属于请求头部,后者属于响应头部。这种方法基于唯一标识,相对来说可以更准确判断文件是否被修改, ETag 的优先级也比 Last-Modified 更高
  • 协商缓存需要配合强制缓存中的 Cache-Control 使用。当缓存没有对应资源时请求服务器

HTTP 和 HTTPs 的区别?

  • 工作端口不同:HTTP 80;HTTPs 443
  • HTTP 明文传输,数据都是未加密的,安全性较差,HTTPs(SSL+HTTP)数据传输过程是加密的,安全性较好
  • 使用 HTTPs 协议需要到 CA 申请证书
  • HTTP 页面响应速度比 HTTPs 快,主要是因为 HTTP 使用 TCP 三次握手建立连接,而 HTTPs 除了 TCP 的三个包,还要加上 SSL 握手的消耗
  • HTTPs 其实就是建构在 SSL/TLS 之上的 HTTP 协议,所以,要比较 HTTPs 比 HTTP 要更耗费服务器资源

HTTPs 的加密与认证过程(TLS 握手过程)?

注意:

  • 前两次握手属于密钥交换,一般而言这个阶段是非对称加密的,加密算法有 RSA 或者 ECDHA,当然后者保证前向安全
  • 在第二次握手中的 CA 证书涉及 CA 机构签名,签名算法会在前两次握手中确认
  • 在第三次握手之后会开始使用对称加密算法,因为非对称加密效率实在不高
  • RSA 有什么弊端:加密性依赖于密钥长度;不保证前向安全,服务器私钥被获取将导致严重后果;RSA 效率并不高

ClientHello

首先,由客户端向服务端发起加密通信请求。客户端主要向服务端发送:

  • 客户端支持的 SSL/TLS 协议版本
  • 客户端产生的的 随机数 (Client Random)
  • 客户端支持的 密码套件列表

ServerHello

服务器收到客户端请求后,向客户端发出响应。服务端回应的内容有:

  • 确认 SSL/TLS 协议版本 (如果浏览器不支持,则关闭加密通信)
  • 服务端生产的 随机数 (Server Random)
  • 确认的 密码套件列表
  • 服务端的 数字证书

CA 证书中包含内容

  • 版本号(Version Number):规范的版本号,如版本 3,值为 0x2
  • 序列号(Serial Number):由 CA 维护的为它所发的每个证书分配的一个序列号,用来追踪和撤销证书。只要拥有签发者信息和序列号,就可以唯一标识一个证书
  • 签名算法(Signature Algorithm):该数字证书支持数字签名所采用的算法,如 sha256RSA
  • 颁发者(Issuer):就是 CA 颁发机构,是签发证书单位的标识信息
  • 有效期(Validity) : 证书的有效期很,包括起止时间
  • 使用者(Subject) : 证书拥有者的标识信息。如果使用的是 OV 或 EV 型证书,就可查看到企业信息,辨别公司真伪
  • 主体的公钥信息(Subject Public Key Info):所保护的公钥相关的信息
  • 使用者可选名称(Subject Alternative Name):即该证书保护的所有域名名称

CA 证书的签名过程以及验证其有效性的过程如下:

  • 首先 CA 会把证书信息打成一个包,然后对这些信息进行 Hash 计算,得到一个 Hash 值
  • 然后 CA 会使用私钥对 Hash 值进行签名得到新的哈希值
  • 最后将证书签名添加到证书内容之后,当客户端使用 CA 公钥解密签名得到 Hash 值,并使用与第一步同样的 Hash 算法对 CA 打包信息进行 Hash 计算,对比两者即可验证 CA 证书的有效性

Client Key Exchange

客户端收到服务端的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务端的数字证书的真实性。如果证书没有问题,客户端会从数字证书中取出服务端的公钥,然后使用它加密报文,向服务端发送如下信息:

  • 一个随机数(Pre-master),该随机数会被服务端公钥加密(RSA/ECDHA)
  • 加密通信算法改变通知,表示随后的信息都将用会话秘钥加密通信
  • 客户端握手结束通知,表示客户端的握手阶段已经结束
  • 之前所有内容的发生的数据做个 摘要,用来供服务端校验

服务端和客户端有了三个随机数,接着用双方协商的加密算法,各自生成本次通信的会话秘钥

Change Cipher Spec & Encrypted Handshake Message

服务端收到客户端的第三个随机数(pre-master key)之后,通过协商的加密算法,计算出本次通信的会话秘钥。服务端向客户端发送最后的信息:

  • 加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信
  • 服务端握手结束通知,表示服务端的握手阶段已经结束
  • 之前所有内容的发生的数据做个 摘要,用来供客户端校验

HTTPs 一定安全可靠吗?

不安全是因为用户点击接受了中间人服务器的证书。中间人服务器与客户端在 TLS 握手过程中,实际上发送了自己伪造的证书给浏览器,而这个伪造的证书是能被浏览器(客户端)识别出是非法的,于是就会提醒用户该证书存在问题。如果用户点击「继续浏览此网站」,相当于用户接受了中间人伪造的证书,那么后续整个 HTTPs 通信都能被中间人监听了。

HTTPs 协议本身到目前为止还是没有任何漏洞的,即使你成功进行中间人攻击,本质上是利用了客户端的漏洞(用户点击继续访问或者被恶意导入伪造的根证书),并不是 HTTPs 不够安全。

HTTP1.0、HTTP1.1、HTTP2 和 HTTP3 的区别?

HTTP1.0

  • 无连接:每次请求都要建立连接,需要使用 keep-alive 参数建立长连接,建立连接十分消耗资源
  • 队头阻塞:HTTP1.0 规定下一个请求必须在前一个请求响应到达之前才能发送,假设前一个请求响应一直不到达,那么下一个请求就不发送,后面的请求就阻塞了
  • 缓存:在 HTTP 1.0 中主要使用 header 里的协商缓存 last-modified\if-modified-since,强制缓存 Expires 来做为缓存判断的标准

HTTP1.1

  • 长连接:好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载
  • 支持管道(pipeline)网络传输:只要第一个请求发出去了,不必等待收到 ACK 响应就可以发第二个请求出去,可以减少整体的响应时间
  • 缓存处理:HTTP1.1 引入了 更多的缓存控制策略(Cache-Control/If-None-Match/ETag),许多可供选择的缓存头来控制缓存策略
  • 断点续传:HTTP1.1 在请求头引入了 range 头域,它允许只请求资源的某个部分,即返回码是 206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接

HTTP2

  • Header 压缩:HTTP 协议的报文由 header + body 构成。想要提高其传输效率的一个办法就是压缩编码。一般而言,body 可以使用诸如 gzip 等算法进行压缩,但 HTTP1 没有考虑头部的压缩。HTTP1 的 header 带有大量信息,而且每次都要重复发送,HTTP2 使用 encoder 来减少需要传输的 header 大小,通讯双方各自 cache 一份 header fields 表,既避免了重复 header 的传输,又减小了需要传输的大小——这就是 HPACK 算法。HPACK 算法主要包括 静态字典(61 种高频字段如 GET/POST/200/http/https 等)、动态字典(62 种以后的字段会被动态更新)、Huffman 编码
  • 二进制帧格式:HTTP2 把请求在应用层切分成 首部和消息负载两类 二进制帧并标上序号,服务器接收到二进制帧后组装成请求进行处理,从而达到并发发送请求的效果。对于服务器端,响应可以通过序号确定是哪个请求,从而不会出现混乱的问题。帧首部(9B)包括帧长度(3B)、帧类型(1B)、标志位(1B)、流标识符(4B,最高位保留,表示该帧属于哪个 Stream)
  • 并发传输:使用类似多路复用的技术,做到同一个连接并发处理多个请求,而且并发请求的数量比 HTTP1.1 大了好几个数量级。一个 TCP 连接中有多个 Stream (Stream 之间有依赖,前面的 Stream 阻塞后面的 Stream);一个 Stream 中包含多个 Message,对应 HTTP1 中的请求或响应;Message 包含多个 Frame,以二进制压缩格式存放 HTTP1 中的内容,对应一个请求的头部或者数据
  • 服务器推送:HTTP2 引入了 server push,它允许服务端推送资源给浏览器,在浏览器明确地请求之前,免得客户端再次创建连接发送请求到服务器端获取。这样客户端可以直接从本地加载这些资源,不用再通过网络主动获取资源。实现的方式可以概括为:客户端发起的 Stream ID 是奇数,服务端则为偶数;当服务器决定使用 Server Push 推送一个资源时(例如 styles.css),它会主动创建一个新的流。在服务器真正开始推送资源数据(即开始使用那个偶数流 ID)之前,它必须先发送一个特殊的 PUSH_PROMISE 帧,这个帧不是在它要创建的新偶数流上发送的!它必须承载在与其关联的客户端原始请求流上(即承载在一个奇数流 ID 上,比如承载在流 ID 1 /index.html 上)。奇偶性完美区分了请求/推送的发起方,避免了流 ID 冲突,并通过 PUSH_PROMISE 将偶数推送流绑定到奇数请求流上,让客户端能正确关联和使用推送的资源

HTTP3(QUIC)

  • HTTP3 直接放弃使用 TCP,将传输层协议改成 UDP,使用 UDP 实现可靠传输
  • 0-RTT:缓存当前会话的上下文,下次恢复会话的时候,只需要将之前的缓存传递给服务器,验证通过,就可以进行传输了 (这是 QUIC 协议相比 HTTP2 的最大优势)
  • 多路复用:首先,HTTP3 的 Stream 是不需要像 HTTP2 一样定义在 Frame Header 中的,它基于 QUIC 定义 Stream,于是它的 Frame Header 变得更加简单(2B,类型 + 长度)。同样地,它的头部压缩算法也更换成了更加高效的 QPACK 算法(静态表更大 + 单向流实时更新动态表)。QUIC 基于 UDP,一个连接上的多个 stream 之间没有依赖,即使丢包,只需要重发丢失的包即可,不需要重传整个连接
  • 更好的移动端表现:QUIC 在移动端的表现比 TCP 好,因为 TCP 是基于 IP 识别连接,而 QUIC 是通过 ID 识别链接。无论网络环境如何变化,只要 ID 不变,就能迅速重新连上
  • 加密认证的报文:TCP 协议头没有经过任何加密和认证,在传输过程中很容易被中间网络设备篡改、注入和窃听。QUIC 的 packet 除了个别报文, 所有报文头部都是经过认证的,报文 Body 都是经过加密的。只要对 QUIC 做任何更改,接收端都能及时发现,有效地降低了安全风险
  • 前向纠错机制前向纠错(Foward Error Correction,FEC) 即每个数据包除了它本身的内容之外还包括了其他数据包的数据,因此少量的丢包可以通过其他包的冗余数据直接组装而无需重传。前向纠错牺牲了每个数据包可以发送数据的上限,但是带来的提升大于丢包导致的数据重传,因为数据重传将会消耗更多的时间(包括确认数据包丢失,请求重传,等待新数据包等步骤的时间消耗)。FEC 采用简单异或的方式,每发送一组数据并对这些数据包依次做异或运算后,将最后的结果作为一个 FEC 包再发送出去。接收方收到一组数据后,根据数据包和 FEC 包即可以进行校验和纠错。比如:10个包编码后会增加2个包,接收端丢失第 2 和第 3 个包,仅靠剩下的 10 个包就可以解出丢失的包,不必重新发送

QUIC 协议的概念和特点?

概念

HTTP3 基于 UDP 协议在应用层实现了 QUIC 协议,具有类似 TCP 的 连接管理、拥塞窗口、流量控制 的网络特性,让 UDP 协议变得可靠

特点

  • 无队头阻塞:QUIC 协议有类似 HTTP2 Stream 与多路复用的概念,可以在 同一条连接上并发传输多个 Stream。UDP 不关心数据包的顺序,也不关心是否丢包。UDP 将每个数据包都有一个序号唯一标识。当某个流中的一个数据包丢失了,该流的其他数据包到达了,数据也无法被 HTTP3 读取,QUIC 重传丢失的报文之后数据才会交给 HTTP3。而其他流的数据报文只要被完整接收,HTTP3 就可以读取到数据。QUIC 连接上的多个 Stream 之间并没有依赖,都是独立的,某个流发生丢包了,只会影响该流,其他流不受影响
  • 快速连接建立:HTTP3 在传输数据前虽然需要 QUIC 协议握手,这个 握手过程只需要 1 RTT,握手的目的是为确认双方的「连接 ID」, 连接迁移就是基于连接 ID 实现的。HTTP3 的 QUIC 协议不与 TLS 分层,QUIC 内部包含了 TLS,它在自己的帧会携带 TLS 记录,只需 1 个 RTT 就可以完成建立连接与密钥协商。在第二次连接的时候,应用数据包可以和 QUIC 握手信息(连接信息 + TLS 信息)一起发送
  • 连接迁移:QUIC 协议没有用四元组的方式来绑定连接,而是通过 连接 ID 来标记通信的两个端点,客户端和服务器可以各自选择一组 ID 来标记自己,因此即使移动设备的网络变化后,导致 IP 地址变化了,只要仍保有上下文信息(比如连接 ID、TLS 密钥等),就可以复用原连接,消除重连的成本,达到了 连接迁移 的功能

QUIC 如何保证可靠传输?

QUIC 协议中 Packet 包含 QUIC Frame,QUIC Frame 包含 HTTP3 Frame。

  • Packet Header:分为 Long Packet Header 用于首次建立连接和 Short Packet Header 用于日常传输数据。QUIC 也是需要三次握手来建立连接的,目的是为了协商连接 ID。协商出连接 ID 后,后续传输时,双方只需要固定住连接 ID,从而实现连接迁移功能。Short Packet Header 中的 Packet Number 每个报文有独一无二的编号,并且严格递增。单调递增的设计,可以让数据包不再像 TCP 那样必须有序确认,当数据包 Packet N 丢失后,只要有新的已接收数据包确认,当前窗口就会继续向右滑动,从而解决了队头阻塞的问题
  • QUIC Frame Header:一个 Packet 报文中可以存放多个 QUIC Frame。用于传输的 Stream Frame 有 Stream ID、Offset 和 length 字段,Stream ID 用于多个并发传输的 HTTP 消息,通过不同的 Stream ID 加以区别、Offset 字段类似于 TCP 协议中的 Seq 序号,保证数据的顺序性和可靠性;Length 标识了 Frame 数据的长度。如果发生丢包了进行重传,通过比较两个数据包的 Stream ID 与 Stream Offset,如果都是一致,就说明这两个数据包的内容一致

所以,QUIC 通过 单向递增的 Packet Number,配合 Stream ID 与 Offset 字段信息,可以支持乱序确认而不影响数据包的正确组装,摆脱了 TCP 必须按顺序确认的限制。

HTTP 的 GET 和 POST 方法区别?

  • GET 一般用来从服务器上获取资源,POST 一般用来更新服务器上的资源
  • GET 是幂等的,即读取同一个资源总是得到相同的数据,而 POST 不是幂等的,因为每次请求对资源的改变并不是相同的
  • GET 不会改变服务器上的资源,而 POST 会对服务器资源进行改变
  • GET 请求的数据会附在 URL 之后,即将请求数据放置在 HTTP 报文的请求头中,以 ? 分割 URL 和传输数据,参数之间以 & 相连;而 POST 请求会把提交的数据则放置在是 HTTP 请求报文的请求体中
  • POST 的安全性要比 GET 的安全性高,因为 GET 请求提交的数据将明文出现在 URL 上,而且 POST 请求参数则被包装到请求体中,相对更安全(实际上这个论断不准确:无论是 GET 还是 POST 底层都是 TCP/IP,GET 和 POST 能做的事情是一样的。你要给 GET 加上 request body,给 POST 带上 url 参数,技术上是完全行的通的。所以对于两者的安全性论断 没有统一答案,两者 本质相同 )
  • 从请求的大小看,GET 请求的长度受限于浏览器或服务器对 URL 长度的限制,允许发送的数据量比较小,而 POST 请求则是没有大小限制的

GET 请求可以带 body 吗?

RFC 规范并没有规定 GET 请求不能带 body。任何请求都可以带 body。GET 请求是获取资源,所以根据这个语义不需要用到 body。URL 中的查询参数也不是 GET 所独有的,POST 请求的 URL 中也可以有参数的。

什么是 XSS 攻击?有什么解决办法?

概念

XSS(Cross(X) Site Scripting, 跨站脚本攻击) 是指 恶意攻击者利用网站没有对用户提交数据进行转义处理或者过滤不足 的缺点,进而添加一些脚本代码嵌入到 web 页面中去,使别的用户访问都会执行相应的嵌入代码,从而盗取用户资料、利用用户身份进行某种动作或者对访问者进行病毒侵害的一种攻击方式

分类

  • 反射性 XSS 攻击(非持久性 XSS 攻击):之所以称为反射型 XSS,是 因为这种攻击方式的注入代码是从目标服务器通过错误信息、搜索结果等等方式“反射”回来的:发出请求时,XSS 代码出现在 URL 中,作为输入提交到服务器端,服务器端解析后响应,XSS 代码随响应内容一起传回给浏览器,最后浏览器解析执行 XSS 代码。这个过程像一次反射,故叫反射型 XSS。而称为非持久型 XSS,则是 因为这种攻击方式具有一次性,由于代码注入的是一个动态产生的页面而不是永久的页面,因此这种攻击方式只在点击链接的时候才产生作用
  • 持久性 XSS 攻击(留言板场景):指 XSS 攻击代码存储在网站数据库,每当用户使用浏览器打开指定页面时,脚本就执行。与非持久性 XSS 攻击相比,持久性 XSS 攻击危害性更大

危害

  • 盗取各类用户帐号,如机器登录帐号、用户网银帐号、各类管理员帐号
  • 控制企业数据,包括读取、篡改、添加、删除企业敏感数据的能力
  • 盗窃企业重要的具有商业价值的资料
  • 非法转账
  • 强制发送电子邮件
  • 网站挂马
  • 控制受害者机器向其它网站发起攻击

防范

漏洞产生的根本原因是 太相信用户提交的数据,对用户所提交的数据过滤不足。解决方案也应该从这个方面入手:

  • 将重要的 cookie 标记为 http only:Javascript 中的 document.cookie 语句就不能获取到 cookie 了(如果在 cookie 中设置了 HttpOnly 属性,那么通过 js 脚本将无法读取到 cookie 信息,这样能有效的防止 XSS 攻击)
  • 表单数据规定值的类型:例如:年龄应为只能为 int 、 name 只能为字母数字组合
  • 对数据进行 HTML 编码处理
  • 过滤或移除特殊的 HTML 标签:例如:
    JavaScript
      <script> , <iframe> , < for < , > for>
    
  • 过滤 JavaScript 事件的标签:例如 "onclick=", "onfocus="

什么是 CSRF 攻击?有什么解决办法?

概念

CSRF 就是 跨站请求伪造。是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与 XSS 非常不同,XSS 利用站点内的信任用户,而 CSRF 则通过伪装成受信任用户请求受信任的网站。

  • 登录受信任网站 A,并在本地生成 Cookie(如果用户没有登录网站 A,那么网站 B 在诱导的时候,请求网站 A 的 api 接口时,会提示你登录)
  • 在不登出 A 的情况下,访问危险网站 B(其实是利用了网站 A 的漏洞)
  • CSRF 攻击

防范

  • Token 验证:服务器发送给客户端一个 token,客户端提交的表单中带着这个 token,如果这个 token 不合法,那么服务器拒绝这个请求
  • 隐藏令牌:把 token 隐藏在 http 的 head 头中
  • Referer 验证:只接受本站的请求,服务器才做响应;如果不是,就拦截

中间人攻击以及如何防范?

概念

指攻击者与通讯的两端分别创建独立的联系,并交换其所收到的数据,使通讯的两端认为他们正在通过一个私密的连接与对方直接对话,但事实上整个会话都被攻击者完全控制。 中间人攻击

  • 嗅探:嗅探或数据包嗅探 是一种用于捕获流进和流出系统/网络的数据包的技术。网络中的数据包嗅探就好像电话中的监听
  • 数据包注入:攻击者会将恶意数据包注入常规数据中。这样用户便不会注意到文件/恶意软件,因为它们是合法通讯流的一部分。在中间人攻击和拒绝式攻击中,这些文件是很常见的
  • 会话劫持:当客户端和服务器端在进行一个会话时,会话中包含了很多重要信息,一些黑客会潜伏在这个会话中,最终控制这个会话,这既是会话劫持
  • SSL 剥离:SSL/TLS 证书通过加密保护着的通讯安全。在 SSL 剥离攻击中,攻击者使 SSL/TLS 连接剥落,随之协议便从安全的 HTTPs 变成了不安全的 HTTP

防范

使用 HTTPs 协议,禁用不安全的 SSL 协议,启用虚拟专用网(VPN)。

通过 HTTPs 双向认证 来避免这种问题:一般 HTTPs 是单向认证,客户端只会验证了服务端的身份,但是服务端并不会验证客户端的身份。如果用了双向认证方式,不仅客户端会验证服务端的身份,而且服务端也会验证客户端的身份。服务端一旦验证到请求自己的客户端为不可信任的,服务端就拒绝继续通信,客户端如果发现服务端为不可信任的,那么也中止通信。

HTTP 1.1 如何优化?

避免发送 HTTP 请求

通过 缓存技术 将请求-响应的数据都缓存在本地,下次请求直接读取本地的数据。

客户端会将第一次请求以及响应的数据保存在本地硬盘,其中将请求的 URL 作为 key,将相应作为 value。当后续发起相同的请求时可以先在本地硬盘上通过 key 查找对应的 value。

服务器在发送 HTTP 响应时会估算一个过期时间,并把信息放到响应头部中。当客户端发现响应过期时会重新向服务器发送请求,如果服务器上资源没有变更,那么服务器会返回 不含有响应体的 304 Not Modified 响应

减少 HTTP 请求次数

  • 减少重定向请求次数:重定向的工作可以交由代理服务器完成
  • 合并请求:一般浏览器会同时发起 5-6 个请求,服务端通过将很多小图片利用 CSS Image Sprites 技术将它们合称为一个大图片,或者通过 Base64 编码将图片嵌入到 HTML 文件实现合并请求
  • 延迟发送请求:只有在用户向下滑动页面的时候再发送请求,对资源 按需获取

减少 HTTP 响应数据大小

  • 无损压缩:去除一些为了代码格式美观而使用的空格、换行;利用霍夫曼编码对资源进行压缩
  • 有损压缩:例如对图片可以使用 WebP 格式压缩、对视频使用 H264、H265 等格式压缩

HTTPs 如何优化?

硬件优化

HTTPs 是 计算密集型,所以应从优化 CPU 入手。可以选择支持 AES-NI 特性的 CPU,因为这种 CPU 在指令级别优化了 AES 算法,加速加密数据的传输。

软件优化

  • 软件升级:升级 Linux 内核版本
  • 协议升级:升级协议软件版本

协议优化

  • 密钥交换算法优化:尽量选用 ECDHE 密钥交换算法替换 RSA 算法
  • TLS 升级:TLS1.3 大幅简化了握手的步骤, 完成握手仅需 1 RTT,安全性也更高。实现方法是 TLS1.3 将 Hello 和公钥交换两个消息合并成了一个消息

证书优化

  • 证书传输优化:尽量选择椭圆曲线 ECDSA 证书
  • 证书验证优化:客户端进行逐级验证证书可靠性是一个复杂的过程,可以着手在这方面进行优化

会话复用

会话复用使用的以下三种技术都有不能避免 重放攻击

  • Session ID:客户端和服务端首次 TLS 握手连接后,双方在内存缓存会话密钥,并用唯一的 Session ID 进行标识。当客户端再次连接时,Hello 报文中会携带 Session ID,只要 ID 没有过期就可以通过 1 RTT 实现握手
  • Session Ticket:服务器不再缓存每个客户端的会话密钥,转而将缓存工作交给客户端。客户端与服务器首次连接时服务器加密会话密钥作为 Ticket 发送给客户端,客户端缓存该 Ticket。客户端再次连接服务器时发送该 Ticket,服务器解码 Ticket 并检查有效期即可恢复通话
  • Pre-shared Key:TLS1.3 通过 Pre-shared Key 实现 0 RTT 完成握手

既然有 HTTP 协议,为什么还要有 RPC?

定义

TCP 是传输层的协议,基于 TCP 造出来的 HTTP 和各类 RPC 协议都只是 定义了不同消息格式的应用层协议 而已。HTTP 协议是 超文本传输协议;RPC 是 远程过程调用,它不是一个具体的协议,而是一种调用方式。虽然大部分 RPC 协议底层使用 TCP,但实际上它们不一定非要用 TCP,也可以改用 UDP 或者 HTTP。HTTP 主要用于 b/s 架构,而 RPC 更多用于 c/s 架构

服务发现

HTTP 中知道服务的域名,就可以通过 DNS 服务去解析得到 IP 地址,默认 80 端口。RPC 一般会有专门的中间服务去保存服务名和 IP 信息,比如 consul 或者 etcd,或者是 redis。想要访问某个服务,就去这些中间服务去获得 IP 和端口信息。由于 dns 也是服务发现的一种,所以也有基于 DNS 去做服务发现的组件,比如 CoreDNS

底层连接方式

HTTP 协议默认在建立底层 TCP 连接之后会一直保持这个连接(keep alive),之后的请求和响应都会复用这条连接。RPC 协议也是通过建立 TCP 长连接进行数据交互,但 RPC 协议一般还会再建个连接池,在请求量大的时候,建立多条连接放在池内,要发数据的时候就从池里取一条连接出来,用完放回去便于下次再复用

传输内容

HTTP 设计初是用于做网页文本展示的,传的内容以字符串为主,有 header 和 body。在 body 这块,它使用 json 来序列化结构体数据, 内容会有冗余。RPC 因为定制化程度更高,可以采用 体积更小 的 protobuf 或其他序列化协议去保存结构体数据,同时也不需要像 HTTP 那样考虑各种浏览器行为,比如 302 重定向。因此性能也会更好一些,这也是在公司内部微服务中抛弃 HTTP,选择使用 RPC 的最主要原因

历史原因

RPC 其实比 HTTP 出现的要早,且比目前主流的 HTTP1.1 性能要更好,所以大部分公司内部都还在使用 RPC。HTTP2 在 HTTP1.1 的基础上做了优化,性能可能比很多 RPC 协议都要好,但由于是这几年才出来的,所以也不太可能取代掉 RPC

既然有 HTTP 协议,为什么还要有 WebSocket?

因为 HTTP 虽然基于支持全双工通信的 TCP 协议,但实际上并没有做到全双工通信,它只允许客户端发送信息给服务器。因此,支持双方全双工通信的 WebSocket 协议应运而生。

WebSocket 概念

WebSocket 是 支持客户端与服务器之间全双工大量数据的通信 的基于 TCP 连接的协议。建立 WebSocket 连接应在 HTTP 请求头部使用 特殊 Header

HTTP
Connection: Upgrade
Upgrade: WebSocket
Sec-WebSocket-Key: YOUR_WEBSOCKET_KEY

上述报文指浏览器希望 升级为 WebSocket协议(Connection: Upgrade),同时带上一段 随机生成的 Base64 码(Sec-WebSocket-Key) 发给服务器。如果服务器正好支持升级成 WebSocket 协议。就会走 WebSocket 握手流程,同时根据客户端生成的 Base64 码,用某个 公开的 算法变成另一段字符串,放在 HTTP 响应的 Sec-WebSocket-Accept 的同时带上 101 状态码 发回给浏览器。HTTP 的响应如下:

HTTP
HTTP/1.1 101 Switching Protocols\r\n
Sec-WebSocket-Accept: YOUR_WEBSOCKET_ACCEPT_CODE
Upgrade: WebSocket\r\n
Connection: Upgrade\r\n

以上的两次 WebSocket 握手使得 WebSocket 连接成功建立,此后客户端与服务端可以通过 WebSocket 协议通信。

WebSocket 的消息格式

数据包在 WebSocket 中被叫做 ,主要有以下字段:

  • opcode:标志这数据帧类型
    • 等于 1:指 text 类型(string)的数据包
    • 等于 2:二进制数据类型([]byte)的数据包
    • 等于 8:关闭连接的信号
  • payload:存放传输的数据的长度,单位是字节
  • payload data:实际传输的数据,根据 payload 长度截取对应的数据

WebSocket 的使用场景

WebSocket 完美继承了 TCP 协议的 全双工 能力,并且提供了解决粘包的方案。它适用于 需要服务器和客户端(浏览器)频繁交互 的大部分场景,比如网页/小程序游戏,网页聊天室,以及一些类似飞书这样的网页协同办公软件。在使用 WebSocket 协议的网页游戏里,怪物移动以及攻击玩家的行为是 服务器逻辑 产生的,对玩家产生的伤害等数据,都需要由 服务器主动发送给客户端,客户端获得数据后展示对应的效果。

评论