- 这是我参与「第三届青训营 -后端场」笔记创作活动的的第四篇笔记
一,HTTP
1,概述
HTTP协议是超文本传输协议(Hyper TextTransfer Protocol)的缩写,它用于Web服务器传输超文本标记语言(HTML)到本地浏览器的传送协议。
HTPP有多个版本,目前广泛使用的是HTTP/1.1版本。
HTTP作为一个应用层协议,一般是监听80端口的
2,特点
- http协议支持客户端/服务端模式,也是一种请求/响应模式的协议。
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。
- 灵活:HTTP允许传输任意类型的数据对象。传输的类型由Content-Type加以标记。
- 无连接:限制每次连接只处理一个请求。服务器处理完请求,并收到客户的应答后,即断开连接,但是却不利于客户端与服务器保持会话连接,为了弥补这种不足,产生了两项记录http状态的技术,一个叫做Cookie,一个叫做Session。
- 无状态:无状态是指协议对于事务处理没有记忆,后续处理需要前面的信息,则必须重传。
3,报文构成
请求报文:
- 请求行:包括请求方法、URL、协议/版本
- 请求头(Request Header)
- 请求正文
响应报文:
- 状态行
- 响应头
- 响应正文
4,HTTP1.0和HTTP1.1的区别
- 缓存处理: 在HTTP1.0中主要使用header里的If-Modified-Since,Expires来做为缓存判断的标准,HTTP1.1则引入了更多的缓存控制策略例如Entity tag,If-Unmodified-Since, If-Match, If-None-Match等更多可供选择的缓存头来控制缓存策略。
- 带宽优化及网络连接的使用: HTTP1.0中,存在一些浪费带宽的现象,例如客户端只是需要某个对象的一部分,而服务器却将整个对象送过来了,并且不支持断点续传功能,HTTP1.1则在请求头引入了range头域,它允许只请求资源的某个部分,即返回码是206(Partial Content),这样就方便了开发者自由的选择以便于充分利用带宽和连接。
- 错误通知的管理: 在HTTP1.1中新增了24个错误状态响应码,如409(Conflict)表示请求的资源与资源的当前状态发生冲突;410(Gone)表示服务器上的某个资源被永久性的删除。
- Host头处理: 在HTTP1.0中认为每台服务器都绑定一个唯一的IP地址,因此,请求消息中的URL并没有传递主机名(hostname)。但随着虚拟主机技术的发展,在一台物理服务器上可以存在多个虚拟主机(Multi-homed Web Servers),并且它们共享一个IP地址。HTTP1.1的请求消息和响应消息都应支持Host头域,且请求消息中如果没有Host头域会报告一个错误(400 Bad Request)。
- 长连接: HTTP 1.1支持长连接(PersistentConnection)和请求的流水线(Pipelining)处理,在一个TCP连接上可以传送多个HTTP请求和响应,减少了建立和关闭连接的消耗和延迟,其中长连接也就是对应在HTTP1.1中的
Connection: keep-alive,一定程度上弥补了HTTP1.0每次请求都要创建连接的缺点。
HTTP1.0和1.1现存的一些问题
- HTTP1.0和HTTP1.1可以称做HTTP1.x,正如上面提到过的,HTTP1.x在传输数据时,每次都需要重新建立连接,无疑增加了大量的延迟时间,特别是在移动端更为突出。
- HTTP1.x在传输数据时,所有传输的内容都是明文,客户端和服务器端都无法验证对方的身份,这在一定程度上无法保证数据的安全性。
- HTTP1.x在使用时,header里携带的内容过大,在一定程度上增加了传输的成本,并且每次请求header基本不怎么变化,尤其在移动端会增加用户流量。
- 虽然HTTP1.1支持了keep-alive,来弥补多次创建连接产生的延迟,但是keep-alive使用多了同样会给服务端带来大量的性能压力,并且对于单个文件被不断请求的服务(例如图片存放网站),keep-alive可能会极大的影响性能,因为它在文件被请求之后还保持了不必要的连接很长时间。同样keep-alive也无法解决线头阻塞(Head-of-line blocking, HOL)问题。
5,HTTPS
HTTP(HyperText Transfer Protocol)是超文本传输协议,它基于 TCP 实现,请求和响应双方都采用约定格式的 Header+Body 进行交互,关于 HTTP 协议的细节本文不再赘述。HTTP 基于明文传输,传输途中存在被监听和篡改的风险;SSL/TLS(Secure Sockets Layer/Transport Layer Security)是安全传输协议,用于保障所传输数据的保密性和完整性;HTTPS(Hypertext Transfer Protocol Secure,常称为 HTTP over SSL|TLS)是安全超文本传输协议,是在 TCP 之上进行了加密之后,再基于 HTTP 传输,它是安全的。
SSL/TLS的主要功能
- 防冒充(pretending): 验证交互双方(通常只是对服务端)身份的真实性;
- 防窃听(eavesdropping): 对传输的数据进行加密,即使被截获也无法被识别;
- 防篡改(tampering): 确保接收方收到的数据是原始未篡改过的,若被篡改能识别出来并丢弃;
TLS
- Client发起一个HTTPS(https:/demo.linianhui.dev)的请求,根据RFC2818的规定,Client知道需要连接Server的443(默认)端口。
- Server把事先配置好的公钥证书(public key certificate)返回给客户端。
- Client验证公钥证书:比如是否在有效期内,证书的用途是不是匹配Client请求的站点,是不是在CRL吊销列表里面,它的上一级证书是否有效,这是一个递归的过程,直到验证到根证书(操作系统内置的Root证书或者Client内置的Root证书)。如果验证通过则继续,不通过则显示警告信息。
- Client使用伪随机数生成器生成加密所使用的会话密钥,然后用证书的公钥加密这个会话密钥,发给Server。
- Server使用自己的私钥(private key)解密这个消息,得到会话密钥。至此,Client和Server双方都持有了相同的会话密钥。
- Server使用会话密钥加密“明文内容A”,发送给Client。
- Client使用会话密钥解密响应的密文,得到“明文内容A”。
- Client再次发起HTTPS的请求,使用会话密钥加密请求的“明文内容B”,然后Server使用会话密钥解密密文,得到“明文内容B”
STL双向认证
- 客户端发起建立HTTPS连接请求,将SSL协议版本的信息发送给服务端;
- 服务器端将本机的公钥证书(server.crt)发送给客户端;
- 客户端读取公钥证书(server.crt),取出了服务端公钥;
- 客户端将客户端公钥证书(client.crt)发送给服务器端;
- 服务器端解密客户端公钥证书,拿到客户端公钥;
- 客户端发送自己支持的加密方案给服务器端;
- 服务器端根据自己和客户端的能力,选择一个双方都能接受的加密方案,使用客户端的公钥加密后发送给客户端;
- 客户端使用自己的私钥解密加密方案,生成一个随机数R,使用服务器公钥加密后传给服务器端;
- 服务端用自己的私钥去解密这个密文,得到了密钥R
- 服务端和客户端在后续通讯过程中就使用这个密钥R进行通信了。
常见的非对称加密算法有:RSA(大素数因式分解困难的原理),DSA(基于整数有限域离散对数难题),ECC
6,SPDY
SPDY是Google在2012年提出的,综合了HTTPS和HTTP两者优点于一体的传输协议,主要解决:
- 降低延迟: 针对HTTP高延迟的问题,SPDY优雅的采取了多路复用(Multiplexing)。多路复用通过多个请求stream共享一个TCP连接的方式,降低了创建多个TCP的延迟同时提高了带宽的利用率。
- 请求优先级(Request Prioritization): 多路复用带来一个新的问题是,在连接共享的基础之上有可能会导致关键请求被阻塞。SPDY允许给每个request设置优先级,这样重要的请求就会优先得到响应。比如浏览器加载首页,首页的html内容应该优先展示,之后才是各种静态资源文件,脚本文件等加载,这样可以保证用户能第一时间看到网页内容。
- header压缩: 前面提到HTTP1.x的header很多时候都是重复多余的,而有些header的内容在不压缩的情况下则比较“庞大”(例如cookie和user-agent等)。选择合适的压缩算法可以减小包的大小和数量,不仅可以节省资源,还可以缩短数据传递的延迟。
- 基于HTTPS的加密协议传输: 保留了HTTPS的TLS加密特性,大大提高了传输数据的可靠性。
- 服务端推送(Server Push): 采用了SPDY的网页,例如我的网页有一个sytle.css的请求,在客户端收到sytle.css数据的同时,服务端会将index.js的文件推送给客户端,当客户端再次尝试获取index.js时就可以直接从缓存中获取到,不用再发请求了。
SYDY将HTTP的内容封装成一种新的frame格式,这样就可以兼容老版本的http,同时可以使用SSL功能,但是SPDY并不是一个标准的协议,不过SPDY开发组的成员参与HTTP2.0的制定
7,HTTP2
HTTP2.0可以说是SPDY的升级版,但是HTTP2.0和SPDY也有不同的地方:
- HTTP2.0消息头的压缩算法采用HPACK算法,而非SPDY采用的DEFLATE算法。
- HTTP2.0设计初期支持明文HTTP传输,而SPDY强制使用HTTPS,到后期两者都需要使用HTTPS。
HTTP2.0新特性:
- 新的二进制格式(Binary Format): HTTP1.x的解析是基于文本。基于文本协议的格式解析存在天然缺陷,文本的表现形式有多样性,要做到健壮性考虑的场景必然很多,二进制则不同,只认0和1的组合。基于这种考虑HTTP2.0的协议解析决定采用二进制格式,实现方便且健壮。
- 多路复用(MultiPlexing): 即连接共享,即每一个request都是是用作连接共享机制的。一个request对应一个id,这样一个连接上可以有多个request,每个连接的request可以随机的混杂在一起,接收方可以根据request的 id将request再归属到各自不同的服务端请求里面。
- header压缩: 如上文中所言,对前面提到过HTTP1.x的header带有大量信息,而且每次都要重复发送,HTTP2.0使用encoder来减少需要传输的header大小,通讯双方各自cache一份header fields表,既避免了重复header的传输,又减小了需要传输的大小。
- 服务端推送(Server Push): 同SPDY一样,HTTP2.0也具有Server Push功能。
(多路复用示意图)
8,HTTP3.0(QUIC)
HTTP3.0,也称作HTTP over QUIC。HTTP3.0的核心是QUIC(读音quick)协议,由Google在2015年提出的SPDY v3演化而来的新协议,传统的HTTP协议是基于传输层TCP的协议,而QUIC是基于传输层UDP上的协议,可以定义成:HTTP3.0基于UDP的安全可靠的HTTP2.0协议。
QUIC解决的问题:
- 减少了TCP三次握手及TLS握手时间: 不管是HTTP1.0/1.1还是 HTTPS,HTTP2.0,都使用了TCP进行传输。HTTPS和HTTP2还需要使用TLS协议来进行安全传输。这就出现了两个握手延迟,而基于UDP协议的QUIC,因为UDP 本身没有连接的概念,连接建立时只需要一次交互,半个握手的时间
- 多路复用丢包时的线头阻塞问题: QUIC保留了HTTP2.0多路复用的特性,但是即使在多路复用过程中,同一个TCP连接上有多个stream,假如其中一个stream丢包,在重传前后续的stream都会受到影响,而QUIC中一个连接上的多个stream之间没有依赖。所以当发生丢包时,只会影响当前的stream,也就避免了线头阻塞问题。
- 优化重传策略: 以往的TCP丢包重传策略是:重传的包使用相同的序号,即如果发送了重传,那么两个重传的包的序号都是N,这将无法判断这个带有编号N的ACK,是否是接收端在收到初始封包后回传的ACK。这就会加大后续的重传计算的耗时。QUIC为了避免这个问题,发送端在传送封包时,初始与重传的每一个封包都改用一个新的编号,unique packet number,每一个编号都唯一而且严格递增,这样每次在收到ACK时,就可以依据编号明确的判断这个ACK是来自初始封包或者是重传封包。
- 流量控制: 通过流量控制可以限制客户端传输资料量的大小,有了流量控制后,接收端就可以只保留相对应大小的接收 buffer,优化记忆体被占用的空间。但是如果存在一个流量极慢的stream ,光一个stream就有可能佔用掉接收端所有的资源。QUIC为了避免这个潜在的HOL Blocking,采用了连线层 (connection flow control) 和 Stream 层的 (stream flow control) 流量控制,限制单一 Stream 可以占用的最大buffer size。
- 连接迁移: TCP连接基于四元组(源 IP、源端口、目的 IP、目的端口),切换网络时至少会有一个因素发生变化,导致连接发生变化。当连接发生变化时,如果还使用原来的 TCP 连接,则会导致连接失败,就得等原来的连接超时后重新建立连接,所以我们有时候发现切换到一个新网络时,即使新网络状况良好,但内容还是需要加载很久。如果实现得好,当检测到网络变化时立刻建立新的 TCP 连接,即使这样,建立新的连接还是需要几百毫秒的时间。 QUIC 的连接不受四元组的影响,当这四个元素发生变化时,原连接依然维持。QUIC 连接不以四元组作为标识,而是使用一个 64 位的随机数,这个随机数被称为 Connection ID,对应每个stream,即使 IP 或者端口发生变化,只要 Connection ID 没有变化,那么连接依然可以维持。
QUIC的0-RTT(DH算法)
在 client 端本地没有任何 server 端信息的时候,是无法做到 0RTT 的,下面先来梳理一下 client 首次和 server 通信的流程:
首次连接
**
**
-
server 生成一个质数
和一个整数
,其中
是
的一个生成元,同时随机生成一个数
作为私钥,并计算出公钥
=
,将
三元组打包成
,等待 client 连接
-
client 首次发起连接,简单发送
给 server
-
server 将已经生成好的
返回给 client
-
client 随机生成一个数
作为自己的私钥,并根据
中的
和
计算出公钥
-
client 计算通信使用的密钥
-
client 用
加密需要发送的业务数据,并带上自己的公钥
一起发送给 server
-
server 计算
,根据笛福赫尔曼密钥交换的原理可以证明两端计算的
是一样的
-
这里不能使用
作为后续通讯的密钥(下面解释),server 需要生成一个新的私钥
,并计算新公钥
,然后计算新的通讯密钥
-
server 用
加密需要返回的业务数据,并带上自己的新公钥
一起发送给 client
-
client 根据新的 server 公钥计算通讯密钥
,并用
解密收到的数据包
-
之后双方使用
作为密钥进行通讯,直到本次连接结束
可以看到,首次连接的时候,在第 3 步时,就已经开始发送实际的业务数据了,而第 1 步和第 2 步正好一去一回花费了 1RTT 时间,所以,首次连接的成本是 1RTT
非首次连接
client 在首次连接后,会把 server 的 存下,之后再次发起连接时,因为已经有
了,可以直接从上面的第 3 步开始,而这一步已经可以发送业务数据了,所以,非首次连接时,QUIC 可以做到 0RTT
K1的必要性
为什么要再生成一个 ,不能直接用
作为后续通讯的密钥?
server 的 是静态配置的,是可以长期使用的,其
和
是提前生成计算好的,为了等待后续 client 连接时计算
,
是不能被销毁的。
想想上面提到的前向安全性,如果攻击者事先记录下了所有通讯过程中的数据包,而后续 server 的 泄漏,那就可以根据公开的
算出
,这样后续的通讯内容就全都可以解密了。而
是由双方动态生成的公私钥对计算得来的,最迟在通讯结束后,双方的临时公私钥对就会销毁,从根本上杜绝了泄漏的可能。
不生成K1的算法叫静态DH算法,生成K1的算法叫DHE算法,DHE算法乘法运算太多,一般使用ECDHE算法(基于椭圆曲线ECC算法的)
笛福赫尔曼密钥交换(DH算法)
参考资料:QUIC 是如何做到 0RTT 的
二,DNS
DNS(Domain Name System)是“域名系统”的英文缩写,他用于将互联网上可读的域名转化为难以记忆但是机器使用的IP地址
1,DNS域名空间结构
域名系统是一个层次结构和分布式数据库,包含各种类型的数据,包含主机名和域名。DNS数据库中的名称形成一个分层树状结构称为域命名空间
DNS 服务器主要分为了根 DNS 服务器、顶级域名 DNS 服务器、权威 DNS 服务器,全球共有 13 个根 DNS 服务器,而每个负责顶级域名的组织都具有一个对应的顶级域名服务器,权威 DNS 服务器则是某些组织自己维护的 DNS 服务器。
上面的这三种 DNS 服务器都会存储区域数据,从而用来查询自己区域内的域名到 IP 地址的映射。
同时,还存在着一种不属于 DNS 层次结构,不存储区域数据,仅仅帮助 DNS 客户端向其他 DNS 服务器进行转发,并将查询结果缓存在本地用于提供更快捷的 DNS 查询的本地 DNS 服务器,它往往由 ISP 所维护,本地 DNS 服务器往往保存了每个根服务器的 IP 地址。这个本地 DNS 服务器的地址是由我们用户所配置在操作系统中的。
DNS 协议一般运行在 53 端口上,一般情况下 DNS 解析优先使用 UDP 进行 DNS 信息的传输,但由于广域网不适合传输过大的 UDP 报文,因此当 UDP 报文大小超过 512 字节时,客户端会采用 TCP 协议来进行 DNS 信息的传输。
为什么用 UDP :
- DNS 查询域名,返回的内容一般不超过 512 字节,使用 UDP 服务器负载更低,响应更快
- DNS 可以容忍数据丢失,当查询内容丢失时,DNS 可能在超时后请求下一个名字服务器或返回解析错误,因此 UDP 比 TCP 更适合
什么情况下用到 TCP
- UDP 报文不足以承载对应的响应数据(UDP 被限制了最大 512 字节),此时由于 UDP 无能为力,因此需要使用 TCP。
- 进行 DNS 区域传输时(对 DNS 数据迁移),要保证可靠的数据交付,因此采用 TCP。
2,DNS解析过程
递归查询:
当本地DNS服务器不知道该域名到IP的映射的时候,会去询问根域名服务器,然后由根域名服务器代替去询问其他顶级域名服务器,然后再获得结果并层层返回
迭代查询:
当本地DNS服务器不知道该域名到IP的映射的时候,会去询问根域名服务器,然后由根域名服务器告诉客户端应该去询问哪个顶级域名服务器,这样每次都是由客户端去询问域名到IP的映射
DNS通常是使用递归查询的,因为递归查询比较简单
- 浏览器缓存:首先浏览器会尝试在自己的缓存中寻找有没有该域名的IP映射的缓存
- 系统缓存:如果浏览器缓存中没有就会查询系统内的缓存有没有该域名IP映射的缓存
- host文件:检查本机的host文件是否存在该域名的映射
- 本地DNS服务器:查询本地的DNS服务器,看看该DNS服务器是否有对应的缓存
- 根服务器:根服务器会判断该域名是由哪个顶级域名服务器(如.com域名)来管辖的,并询问对应的顶级域名服务器
- 顶级服务器:递归询问下一级域名服务器,直到找到域名的映射,或者查询不到结果,再层层递归返回给本地DNS服务器
- 存入缓存:本地DNS服务器将获得到的结果加入到缓存,已备下次使用
三,DHCP
DHCP(Dynamic Host COnfiguration Protocol)动态主机协议,是一个应用层协议,基于 UDP, 机可以通过 DHCP 自动获取一个 IP 地址,它使用了 68 端口进行发送,67 端口进行接收。
DHCP的实现分为4步,分别是:
- Client端在局域网内发起一个DHCP Discover广播包,目的是想发现能够给它提供IP的DHCP Server。
- 可用的DHCP Server接收到Discover包之后,通过发送DHCP Offer包给予Client端应答,意在告诉Client端它可以提供IP地址(同样是广播)。
- Client端接收到Offer包之后,向一个或者多个服务端中的一个服务端发送DHCP Request包请求分配IP。
- DHCP Server发送ACK数据包,确认信息。
攻击:
DHCP starvation attack,DHCP饥饿攻击 :
通过伪造合法的MAC地址,不断地向DHCP服务器发出DHCP Request包,最后耗尽服务器的可用IP,于是原有的这台DHCP服务器便不能够给客户端分配IP了,此时不法分子再伪造一台DHCP服务器,给客户端分配IP,将客户端的默认网关和DNS都设置成自己的机器,于是便可以对客户端进行中间人攻击。
参考资料: