HTTP篇
HTTP篇
追随HTTP的发展与演变,由浅入深的学习和理解HTTP
HTTP基本概念
- HTTP是什么?
HTTP 是超文本传输协议,全称 HyperText Transfer Protocol.
- 超文本:超越了普通的文本的文本,包含文字、图片、视频等的混合体。最关键的超链接:从一个超文本跳转到另一个超文本。
- 传输:两点之间数据的传输,HTTP是双向协议(即双方都需要遵守的),且只要中间人同样遵守HTTP协议,就能实现数据的传递。
- 协议:HTTP是一个应用于计算机世界里的协议,利用计算机的语言确立了计算机之间(两个以上的参与者)交流通信的规范,并通过相关的控制和错误处理方式来实现计算机的行为约定和规范。
小结:HTTP是一个在计算机世界里在两点之间传输文字、图片、音视频、超链接等超文本数据的约定和规范。
- HTTP常见状态码:
- 200 OK :成功状态码
- 204 Not Content :响应头没有body的成功状态码
- 206 Partial Content :表示相应返回body数据并不是资源全部,也是服务器处理成功的状态
- 301 Moved Permanently :表示永久重定向,说明请求资源已经不存在了,需改用新的URL再次访问。
- 302 Found :临时重定向,请求的资源还在,暂时需要用另一个URL来访问
** 注意:301、302都会在响应头里使用字段Location
,指明后续要跳转的URL,浏览器会自动重定向新的URL。** - 304 Not Modified :表示资源未修改,直接重定向已存在的缓冲文件,即缓存重定向(不具有跳转的含义)。
- 400 Bad Request:表示客户端请求的报文有错误,但只是个笼统的错误。
- 403 Forbidden :表示服务器禁止访问资源,并不是客户端的请求出错。
- 404 Not Found :表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。
- 500 Internal Server Error :服务器端发生了错误,笼统的错误码。
- 501 Not Implemented :表示客户端请求功能还不支持,类似“即将开业,敬请期待”
- 502 Bad Gateway :通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误(例如nginx正常工作,tomcat相应异常)。
- 503 Service Unavailable :表示服务器忙
- HTTP常见字段:
Host 字段:客户端发送请求时,指定服务器的域名。
Content-Length 字段:表明本次回应的数据长度(这里没有是因为是get请求,没有数据)。
粘包问题
背景:HTTP协议是基于TCP传输协议进行通信的,而使用TCP传输协议,就会存在粘包问题。
问题描述:多个应用层的消息在 TCP 层被合并成一个字节流一起发送,接收端读取时无法区分每个消息的边界。
例如:
客户端发送的数据:hello world
、hello jack
、hello alice
在基于TCP传输时,这些消息有可能被合并成一个字节流一起发送。
服务器端连续收到的内容:hello worldhello jackhello alice
,接受方不知道从哪里截断每条信息,这个现象就是粘包。
解决:
- 固定长度的消息
- 特殊字符作为边界
- 自定义消息结构
HTTP协议通过设置回车符、换行符作为 HTTP header 的边界,通过 Content-Length 字段作为HTTP body 的边界,这两个方式都是为了解决“粘包”问题。
Connection 字段 :通常用于客户端要求服务器使用HTTP 长连接机制(长连接的特点是:只要任意一端没有明确提出断开连接,则保持TCP连接状态)。
HTTP/1.1 版本默认连接是长连接,但为了兼容老版本的http,需要指定Connection
首部字段的值为 Keep-Alive
。
注意区分 TCP 的 Keepalive 和 HTTP 的 Keep-Alive。
Content-Type 字段:表示服务器回应时,告诉客户端,本次数据是什么格式。
Accept 字段:客户端请求时,声明自己可以接受哪些数据格式。
Content-Encoding 字段:表示服务器返回的数据的压缩方式。
Accept-Encoding:客户端在请求时,说明自己可以接受哪些压缩方法。
GET与POST
- get 与 post 的区别
- get 的语义是从服务器获取指定的资源,请求参数只允许ASCII 字符,放在URL中。
- post 的语义是根据请求负荷(报文body)对指定的资源做出处理,携带的数据位于报文body中,可以是任意格式(客户端与服务器协商一致即可)
- 安全和幂等
概念:
- 安全,在HTTP协议中指请求方法不会“破坏”服务器上的资源。
- 幂等,指多次执行相同操作,结果都是相同的。
GET与POST: - get 是安全且幂等的,它只做读操作。所以,可以对get请求的数据做缓存,这个缓存可以存到浏览器本身上(彻底避免浏览器发送请求),也可以做到代理上(nginx),而且在浏览器中get请求可以保存作为书签。
资源过期或数据不一致的解决:- 缓存验证机制:浏览器缓存资源后,下次请求并不一定直接使用缓存,先询问服务器“缓存是否还有效”。
- 设置合理的强缓存:资源访问频繁则设置长时间缓存,反之设置短时间缓存或者禁用强缓存。
- 文件名加 hash,更新就换文件名(前端最常用)。
- 重要数据用post、websocket、ajax等方式动态请求
- post 是新增或提交数据的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据会创建多个资源,同样也不是幂等的。所以,浏览器一般不会缓存post请求,也不能把post请求保存为书签。
- 注意:
- 如果安全是指信息是否会泄露的话,那么get与post数据都不安全,get的数据是直接在浏览器地址栏就可以看到。同样地,post的数据也是明文,虽然地址栏看不到,但是只要抓个包,查看body的数据就可以看到了。所以,要避免传输过程数据被窃取,就要使用HTTPS协议。
- get请求同样可以带body,post请求的url中也可以有参数,只是规范,不是必须。
HTTP缓存技术
-
HTTP缓存的实现方式
HTTP缓存:对于幂等的HTTP请求,把这对请求-响应的数据缓存在本地,下次就直接读取本地的数据,不必通过网络获取服务器的响应了。
HTTP通过强制缓存与协商缓存实现缓存。 -
强制缓存:
浏览器对缓存是否过期进行判断,决定是否使用缓存的主动性在浏览器这边。
这里的自service worker 就是使用了强制缓存。
强制缓存通过下面两个HTTP响应头部字段实现的,用来表示资源在客户端缓存的有效期:
Cache-Control
,是一个相对时间Expires
,是一个绝对时间
如果同时拥有这两个字段,Cache-Control 优先级高于 Expires。
Cache-Control 的使用:- 浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 Cache-Control,Cache-Control 中设置了过期时间大小
- 浏览器再次请求访问服务器中该资源时,会先通过请求资源的时间与 Cache-Control 中设置的过期时间来判断该资源是否过期,如果没有,则直接使用,否则重新请求服务器
- 如果过期,发送请求到服务器,服务器收到请求后,会再次更新 Response 头部的 Cache-Control
- 协商缓存
服务器端告诉客户端是否可以使用缓存的方式称为协商缓存。
协商缓存的实现:
-
请求头部中的
If-Modified-Size
字段与响应头部中的Last-Modified
字段实现,这两个字段的意思是:- 响应头部中的
Last-Modified
:标识这个响应资源的最后修改时间 - 请求头部中的
If-Modified-Size
:当资源过期了,发现响应头中具有 Last-Modified 声明,则再次发起请求,同时带上 Last-Modified 的时间,服务器收到请求后发现有 If-Modified-Size 则与被请求资源的最后修改时间对比,如果最后修改时间较新,说明资源右改动过了,则返回最新资源,HTTP 200 OK;否则,响应 304 走缓存。
- 响应头部中的
-
请求头部中的
If-None-Match
字段与响应头部中的ETag
字段,这两个字段的意思是:- 响应头部中
ETag
:唯一标识响应资源 - 请求头部中的
If-None-Match
:资源过期时,浏览器发现响应头里头Etag,则再次向服务器发起请求,同时会将请求头 If-None-Match 的值设置为 Etag 的值。服务器收到请求后对比唯一标识 Etag,返回相应的304或200。
- 响应头部中
小结:第一种实现方式是基于时间实现的,第二种则是基于唯一标识实现的,相对来说后者更加可靠。
注意:两种方式同时出现时,优先判断 Etag ,Etag变了则直接返回新资源,否则再判断Last-Modified。
为什么 Etag 优先级更高?
因为 Etag 能解决 Last-Modified 难以解决的问题:
- 没有修改文件内容的情况下,最后修改时间也发生了变动
- 有些文件在秒级以内修改,If-Modified-Since 感知不到
- 有些服务器不能精确获取文件最后修改时间
注意:协商缓存这两个字段都需要配合强制缓存中的 Cache-Control 字段来使用,只有在未能命中强制缓存的时候,才能发起带有协商缓存字段的请求。
使用ETag字段实现协商缓存的过程:
- 浏览器第一次访问服务器时,服务器会根据当时的资源生成唯一标识ETag,加在Response头部。
- 当浏览器再次请求访问服务器时,首先会检查强制缓存是否过期
- 如果没有过期,直接使用本地缓存
- 过期了,则把ETag加在请求头部中的if-none-match字段中
- 服务器再次收到请求后,根据Etag与当前请求资源生成的唯一标识对比
- 如果相等,则返回 304
- 不等,则返回 200 并返回资源与 ETag
- 如果浏览器收到304的请求响应状态码,则直接加载本地缓存,否则更新资源。
HTTP特性
HTTP 常见的版本有 HTTP/1.1,HTTP/2.0,HTTP/3.0,不同版本的 HTTP 特性是不一样的。
- HTTP/1.1
-
优点:简单、灵活和易于扩展、应用广泛和跨平台
- 简单:报文格式
header + body
,头部信息也是key-value
简单文本的形式。 - 灵活和易于扩展:HTTP 协议里的各类请求方法、URL/URI、状态码、头字段等每个组成要求都没有被固定死,允许开发人员自定义和扩充。同时HTTP工作在应用层,则它下层可以随意变化,例如:HTTPS 就是在 HTTP 与 TCP 之间增加了 SSL/TLS 安全传输层;
- 应用广泛和跨平台
- 简单:报文格式
-
缺点:无状态(双刃剑)、明文传输(双刃剑)、不安全
- 无状态:不需要记忆HTTP状态,能减轻服务器的负担;同时,由于无状态,导致关联性的操作会非常麻烦。例如:登录=>添加购物车=>下单=>结算=>支付,如果是无状态的,服务器不知道其关联,则每次都要问一遍身份信息。
解决方案:Cookie 技术:在请求和响应报文中写入 Cookie 信息来控制客户端的状态。
- 无状态:不需要记忆HTTP状态,能减轻服务器的负担;同时,由于无状态,导致关联性的操作会非常麻烦。例如:登录=>添加购物车=>下单=>结算=>支付,如果是无状态的,服务器不知道其关联,则每次都要问一遍身份信息。
- 明文传输:方便阅读、调试便利;信息裸奔,无隐私、不安全
- 不安全:
- 通信用明文,内容被窃听。比如,账号信息容易泄露。
- 不验证通信方身份,遭遇伪装。比如,访问假的淘宝、拼多多
- 无法证明报文的完整性,有可能被篡改。比如,网页上植入垃圾广告
解决:用HTTPS的方式解决,也就是引入SSL/TLS层,使得在安全上达到了极致。
- 性能:
基于TCP/IP,使用了请求-应答的通信模式,性能关键在这两点。- 长连接
- 管道网络传输:在同一个TCP连接里,客户端可以发起多个请求,只要第一个请求发出去了,不必等其回来,就可以发送第二个请求,可以减少整体的响应时间。(服务器必须按照接受请求的顺序发送对这些管道化请求的响应,这导致了如果A请求比较耗时,后续的请求都会被阻塞住,这称为对头堵塞)
注意:HTTP/1.1管道解决了请求的对头阻塞,没有解决响应的对头阻塞。并且该技术不是默认开启,后续讨论 HTTP/1.1 都是建立在没有使用管道化的前提。 - 队头阻塞
小结:HTTP/1.1 性能一般,后续的HTTP/2和HTTP/3都是在优化HTTP性能。
HTTP与HTTPS
- 区别:
- HTTP 是超文本传输协议,信息是明文传输,存在安全风险。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
- HTTP 在 TCP 三次握手后便可以进行 HTTP 报文传输;而 HTTPS 在 TCP 三次握手之后,还需要进行SSL/TLS 的握手,才可以进入加密报文传输。
- HTTP 默认端口80,HTTPS 默认端口号443。
- HTTPS 协议需要向CA(证书权威机构)申请数字证书,保证服务器的身份是可靠的。
- HTTPS 解决了 HTTP哪些问题
HTTP 由于是明文传输,安全上存在三个风险:
- 窃听风险
- 篡改风险
- 冒充风险
加入 SSL/TLS 协议,可以很好的解决上述风险:
- 信息加密:交互信息无法被窃取,但你的账号会因为自身忘记而丢失。
- 校验机制:无法篡改通信内容,篡改了就不能正常显示
- 身份证书:保证淘宝是真的淘宝
HTTPS 是如何解决上面的三个风险的?
- 混合加密实现机密性,解决了窃听的风险
- 摘要算法保证完整性,解决了篡改风险
- 将服务器公钥放入到数字证书中,解决了冒充的风险
混合加密:
- 在通信建立前采用非对称加密生成会话密钥
- 在通信过程中使用会话密钥对明文进行对称加密
摘要算法+数字签名:
注意:哈希算法可以确保内容不会被篡改,但是并不能保证内容+哈希值不会被中间人替换,因为这里缺少对客户端收到的信息是否来源服务端的证明。
所以需要引入数字签名(私钥加密,公钥解密)来保证内容不被替换。
注意:这里加密的不是内容本身(非对称加密解密太耗时),而是对内容的哈希值加密。
数字证书:
如果攻击者伪造私钥公钥,也能篡改内容,所以这里引入数字证书来对身份进行验证。
将公钥+个人信息到第三方权威加密生成数字签名,并打包成数字证书,在发送内容时,不仅要用私钥对内容签名,还会发送数字证书。接收方收到后,先拿去数字证书到第三方权威机构去认证,如果是合法,才能证明源自真正的发送方。
- HTTPS是如何建立连接的?期间交互了什么?
SSL/TLS 协议基本流程:
- 客户端向服务器索要并验证服务器公钥
- 双方协商生成会话密钥
- 双方采用会话密钥进行加密通信
TLS的握手阶段通过四次通信实现前两步,使用不同密钥交换算法,流程不一样,现在常用交换算法有:RSA 算法和ECDHE算法。
基于RSA算法的TLS握手过程:
客户端校验数字证书的流程
证书信任链
我们向CA申请的证书一般不是根证书签发的,而是由中间证书签发的,比如百度的证书,下图展示了三级证书:
证书信任链验证流程:
为什么证书链这么麻烦,Root CA不直接颁发证书?
为了确保根证书的绝对安全,将根证书隔离地越严格越好,不然根证书如果失守了,那么整个信任链都会有问题。
- HTTPS 的应用数据是如何保证完整性的?
TLS 在实现上分为握手协议和记录协议两层:
- TLS握手协议(即TLS四次握手过程),负责协商加密算法和生成对称密钥,后续用此密钥来保护应用程序数据(HTTP数据)。
- RLS记录协议负责保护应用程序数据并验证其完整性和来源,所以对HTTP数据加密是使用记录协议。
TLS记录协议主要负责消息(HTTP数据)的压缩,加密以及数据的认证,过程如下图:
记录协议完成后,最终的报文数据将传递到传输控制协议TCP层进行传输。
- HTTPS 一定安全可靠吗?
背景:字节一面面试官提问:如果有假基站起了转发全部信息的作用,这样是不是假基站就获取到全部信息,从而造成信息泄露。
场景分析:客户端通过浏览器向服务端发起HTTPS请求时,被假基站转发到了一个中间人服务器,于是客户端和中间人服务器完成了TLS握手,然后这个中间人服务器再与真正的服务端完成TLS握手。
这种场景是有前提的,前提是用户点击接受了服务器的证书,就是图中红圈部分。
所以,HTTPS 协议本身到目前为止还是没有任何漏洞的,即使成功进行了中间人攻击,本质上是利用率客户端的漏洞(用户点击继续访问或者被恶意导入伪造的根证书)
为什么抓包工具能截取HTTPS数据?
原理与中间人攻击一致。对于HTTPS连接来说,中间人攻击需要满足:
- 中间人,作为客户端与真实服务器端建立连接,这一步不会有问题,因为服务端不会校验客户端的身份;
- 中间人,作为服务端与真实客户端建立连接,这里就有客户端信任服务端的问题,也就是服务端必须有对应域名的私钥,而要拿到私钥只有:
- 去网站服务端拿到私钥
- 去CA处那域名签发私钥
- 自己办法证书,且要被浏览器信任(起到CA的作用)
抓包工具只能采用第三种方法取得中间人的身份:在客户端安装 Fiddler 的根证书,客户端就会拿着中间人签发的证书去中间人自己的CA去认证。
如何避免被中间人抓取数据?
HTTPS 双向认证:
HTTP/1.1、HTTP/2、HTTP/3 的演变
- HTTP/1.1 相比 HTTP/1.0 性能上的改进:
- 使用长连接改善了 HTTP/1.0 短连接造成的性能开销
- 支持管道 (pipeline) 网络传输,只要第一个请求发出去了,不必等其回来,就可以发送后面的请求,可以减少整体的响应时间
缺点:
- 请求/相应头(Header)未经压缩就发送,首部信息越多延迟越大,只能压缩body部分
- 发送冗长的首部,每次互相发送相同的头部造成浪费
- 服务器按请求顺序响应,如果服务器响应慢,导致客户端一直请求不到数据,也就是队头阻塞
- 没有请求优先级的控制
- 请求只能从客户端开始,服务器只能被动响应
- HTTP/2 的优化
HTTP/2协议是基于HTTPS的,安全性有保障。
相比HTTP/1.1的改进:
- 头部压缩:压缩头部,并且相同头部会去重(通过HPAC算法实现:客户端与服务器同时维护一张头信息表,所有字段存入该表,生成相应的索引号,通过发送索引号的方式替代相同字段,提高速度)。
- 二进制格式:不再像 HTTP/1.1 中的纯文本形式的报文,全面采用二进制格式,头信息和数据体都是二进制,并且统称为帧:头信息帧 (Headers Frame) 和数据帧 (Data Frame)。计算机无需将明文的报文转成二进制,而是直接解析二进制报文,增加了数据传输效率。(这里面还有静态表,编码方式的学问)
- 并发传输:HTTP/1.1中的管道只解决了请求队头阻塞,并没有解决相应队头阻塞,所以本质上没有解决问题;彻底解决问题的是HTTP/2基于帧 (frame) +流 (stream) 采用多路复用的解决了队头阻塞:一个TCP连接中多个请求可以并发发送、并发响应、无顺序限制。
一个TCP包含多个stream,stream中可以包含一个或多个Message(即请求或响应)。针对不同的HTTP请求用独一无二的Stream ID来区分,接收端通过Stream ID有序组装为HTTP消息,不同的 stream 的帧是可以乱序发送的,因此可以并发不同 stream,也就是说HTTP/2可以并行交错地发送请求和响应。
- 服务器主动推送资源:服务器也可以主动向客户端发送消息,客户端和服务器双方都可以建立stream,stream ID 通过奇偶来区分。
比如,客户端请求HTML文件后通常还需要CSS来渲染页面,HTTP/1.1中需要两次消息往返,而HTTP/2中,客户端在访问HTML时,服务器可以主动推送CSS文件,减少了消息的传递次数。
HTTP/2的缺陷:
还是队头阻塞问题,只不过不是在HTTP层,而是在TCP层:
HTTP/2 是基于TCP协议传输数据,TCP是字节流协议,TCP层必须保证收到的字节数据是完整且连续的,这样内核才会将缓冲区里的数据返回给HTTP应用,所以分帧传输当前一个字节没有到达时,后面收到的字节数据只能存放在内核缓冲区中,只有所有字节数据到达后,http/2应用层才能从内核拿到数据。
一旦发生了丢包,就会触发TCP重传机制,这样一个TCP连接中的所有HTTP请求都必须等待这个丢了的包被重传回来。而一个TCP连接中有很多流。
3. HTTP/3 的优化
HTTP/3把HTTP下层的TCP协议改成了UDP
基于UDP的QUIC协议可以实现类似TCP的可靠性传输,QUIC特点:
- 无队头阻塞:当某个流丢包时,只会阻塞这个流,其他流不受影响。
- 更快建立连接:HTTP/1和HTTP/2协议的TLS与TCP分层,需要分批次来握手,先TCP握手,再TLS;HTTP/3在传输数据前只需要进行1RTT(往返时延)的QUIC协议握手,确认双方连接ID,便于实现连接迁移。HTTP/3内部包含了TLS(而不是与他分层),它会在自己的帧里携带TLS,加上QUIC使用的是TLS/1.3,因此只需要一个RTT就可以完成建立连接与密钥协商。
甚至在第二次连接时,应用数据包可以和QUIC握手信息(连接信息+TLS信息)一起发送,达到0 RTT的效果。
- 连接迁移:TCP传输协议是通过四元组(源IP、源端口、目的IP、目的端口)确定一条TCP连接
当移动设备网络从4G切换到WIFI时,意味着ip地址变换,必须断开连接,重新建立连接。而建立连接的过程包含TCP三次握手、TLS四次握手的时延,以及TCP慢启动的减速过程(拥塞控制:避免网络拥堵降低初始发送速率),给用户的感觉就是网络突然卡顿了,因此连接迁移成本高。
而QUIC通过连接ID来标记通信的两个端点,客户端和服务器端有各自的ID标记自己,即使移动设备网络变化导致IP地址变化,只要保存上下文信息(比如连接id,TLS密钥等),就可以无缝地复用原来的连接,实现连接迁移。
QUIC是一个在UDP之上的伪TCP+TLS+HTTP/2的多路复用的协议
注意:QUIC是新协议,对于很多网络设备,不知道QUIC,所以会把它当作UDP。而有的网络设备是会丢掉UDP包的,所以QUIC包就会被丢弃。