持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 11 天,点击查看活动详情
🍳引言
面试四大宝典之一的计网算是重中之重,其中网络层、运输层、应用层属于重点,本篇我们就应用层里边的HTTP展开,谈谈HTTP中常见状态码,缓存技术、HTTPS如何解决HTTP的安全问题、混合加密 && 中间人攻击 ~
🎯超文本
🎯常见状态码
2xx
- 201 Created :请求被成功处理并且在服务端创建了一个新的资源。比如我们通过 POST 请求创建一个新的用户。
- 202 Accepted :服务端已经接收到了请求,但是还未处理。
- 204 No Content : 服务端已经成功处理了请求,但是没有返回任何内容。
- 描述的是我们向服务端发送 HTTP 请求之后,只关注处理结果是否成功的场景。也就是说我们需要的就是一个结果:true/false。
3xx【重定向】
有一个location,浏览器会自动重定向到新的url 304:缓存方面的
4xx【客户端错误状态码】
400:客户端请求的报文有错误,比如请求参数不合法、请求方法错误。 401 Unauthorized : 未认证却请求需要认证之后才能访问的资源 403:服务器禁止访问,而不是客户端请求失败 409 Conflict : 表示请求的资源与服务端当前的状态存在冲突,请求无法被处理。
5xx【服务端错误】
客户端请求报文正确,但是服务器处理内部发生了错误
502:比如nginx负载均衡那台机子,本身没有错误,但是继续跳转下游的服务器时,下游服务器不能提供服务,或者返回了错误
503:I am busy
🎯HTTP字段
host
表名要请求哪个域名
Connection 字段
Connection 字段最常用于客户端要求服务器使用 TCP 持久连接,以便其他请求复用。
Content-Type
客户端请求的时候,可以使⽤ Accept 字段声明⾃⼰可以接受哪些数据格式。
【Accpet:/】,客户端声明⾃⼰可以接受任何格式的数据。
Content-Encoding
🎯🎐HTTP缓存
HTTP 缓存有哪些实现方式?
对于一些具有重复性的 HTTP 请求,比如每次请求得到的数据都一样的,我们可以把这对「请求-响应」的数据都缓存在本地,那么下次就直接读取本地的数据,不必在通过网络获取服务器的响应了,这样的话 HTTP/1.1 的性能肯定肉眼可见的提升。
所以,避免发送 HTTP 请求的方法就是通过缓存技术,HTTP 设计者早在之前就考虑到了这点,因此 HTTP 协议的头部有不少是针对缓存的字段。
HTTP 缓存有两种实现方式,分别是强制缓存和协商缓存。
什么是强制缓存?
强缓存指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边。
如下图中,返回的是 200 状态码,但在 size 项中标识的是 from disk cache,就是使用了强制缓存。
强缓存是利用下面这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期:
Cache-Control, 是一个相对时间;Expires,是一个绝对时间;
如果 HTTP 响应头部同时有 Cache-Control 和 Expires 字段的话,Cache-Control的优先级高于 Expires 。
Cache-control 选项更多一些,设置更加精细,所以建议使用 Cache-Control 来实现强缓存。具体的实现流程如下:
- 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 Cache-Control,Cache-Control 中设置了过期时间大小;
- 浏览器再次请求访问服务器中的该资源时,会先通过请求资源的时间与 Cache-Control 中设置的过期时间大小,来计算出该资源是否过期,如果没有,则使用该缓存,否则重新请求服务器;
- 🎆服务器再次收到请求后,会再次更新 Response 头部的 Cache-Control。
什么是协商缓存?
当我们在浏览器使用开发者工具的时候,你可能会看到过某些请求的响应码是 304,这个是告诉浏览器可以使用本地缓存的资源,通常这种通过服务端告知客户端是否可以使用缓存的方式被称为协商缓存。
上图就是一个协商缓存的过程,所以协商缓存就是与服务端协商之后,通过协商结果来判断是否使用本地缓存。
基于时间
第一种:请求头部中的 If-Modified-Since 字段与响应头部中的 Last-Modified 字段实现,这两个字段的意思是:
- 响应头部中的
Last-Modified:标示这个响应资源的最后修改时间; - 请求头部中的
If-Modified-Since: 当资源过期了,发现响应头中具有 Last-Modified 声明,则再次发起请求的时候带上 Last-Modified 的时间,服务器收到请求后发现有 If-Modified-Since 则与被请求资源的最后修改时间进行对比(Last-Modified) 1、如果最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK; 2、如果最后修改时间较旧(小),说明资源无新修改,响应 HTTP 304 走缓存。
基于唯一标识
第二种:请求头部中的 If-None-Match 字段与响应头部中的 ETag 字段,这两个字段的意思是:
- 响应头部中
Etag:唯一标识响应资源; - 请求头部中的
If-None-Match: 当资源过期时,浏览器发现响应头里有 Etag,则再次向服务器发起请求时,会将请求头If-None-Match 值设置为 Etag 的值。服务器收到请求后进行比对,如果资源没有变化返回 304,如果资源变化了返回 200。
第一种实现方式是基于时间实现的,第二种实现方式是基于一个唯一标识实现的,相对来说后者可以更加准确地判断文件内容是否被修改,避免由于时间篡改导致的不可靠问题。
如果在第一次请求资源的时候,服务端返回的 HTTP 响应头部同时有 Etag 和 Last-Modified 字段,那么客户端再下一次请求的时候,如果带上了 ETag 和 Last-Modified 字段信息给服务端,这时 Etag 的优先级更高,也就是服务端先会判断 Etag 是否变化了,如果 Etag 有变化就不用在判断 Last-Modified 了,如果 Etag 没有变化,然后再看 Last-Modified。
**为什么 ETag 的优先级更高?**这是因为 ETag 主要能解决 Last-Modified 几个比较难以解决的问题:
- 在没有修改文件内容情况下文件的最后修改时间可能也会改变,这会导致客户端认为这文件被改动了,从而重新请求;
- 可能有些文件是在秒级以内修改的,
If-Modified-Since能检查到的粒度是秒级的,使用 Etag就能够保证这种需求下客户端在 1 秒内能刷新多次; - 有些服务器不能精确获取文件的最后修改时间。 注意,协商缓存这两个字段都需要配合强制缓存中 Cache-control 字段来使用,只有在_未能命中强制缓存的时候_,才能发起带有协商缓存字段的请求。
下图是强制缓存和协商缓存的工作流程:
🎈🎐具体过程
总结:缓存过期了,带上之前缓存时的版本号,跟服务端的最新版本号【重新计算出来的ETag】比较,
若一样则说明未更改过,可以使用缓存
当使用 ETag 字段实现的协商缓存的过程:
- 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的;
- 当浏览器再次请求访问服务器中的该资源时,首先会先检查强制缓存是否过期:
- 如果没有过期,则直接使用本地缓存;
- 如果缓存过期了,会在 Request 头部加上 If-None-Match 字段,该字段的值就是 ETag 唯一标识;
- 服务器再次收到请求后,会根据_请求中的 If-None-Match 值_与_服务端重新对当前请求的资源重新计算出来的ETag_进行比较
- 如果值相等,则返回 304 Not Modified,不会返回资源,浏览器直接从本地缓存拿即可;
- 如果不相等,则返回 200 状态码和返回资源,并在 Response 头部加上新的 ETag 唯一标识;
- 如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。
🎐HTTP的缺点
无状态
可以用cookie来解决
明文传输
不安全
HTTP ⽐较严重的缺点就是不安全:
通信使⽤明⽂(不加密),内容可能会被窃听。⽐如, 账号信息容易泄漏,那你号没了。 不验证通信⽅的身份,因此有可能遭遇伪装。⽐如, 访问假的淘宝、拼多多,那你钱没了。 ⽆法证明报⽂的完整性,所以有可能已遭篡改。⽐如, ⽹⻚上植⼊垃圾⼴告,视觉污染,眼没了。
- HTTP 的安全问题,可以⽤ HTTPS 的⽅式解决,也就是通过引⼊ SSL/TLS 层,使得在安全上达到了极致。
🎈HTTPS
信息加密
混合加密
采用非对称加密和对称加密结合的混合加密
通信阶段:使用非对称加密
公钥加密,私钥解密
SSL/TLS 的核心要素是非对称加密。非对称加密采用两个密钥:一个公钥,一个私钥。在通信时,私钥仅由解密者保存,公钥由任何一个想与解密者通信的发送者(加密者)所知。可以设想一个场景,
在某个自助邮局,每个通信信道都是一个邮箱,每一个邮箱所有者都在旁边立了一个牌子,上面挂着一把钥匙:这是我的公钥,发送者请将信件放入我的邮箱,并用公钥锁好。
但是公钥只能加锁,并不能解锁。解锁只能由邮箱的所有者——因为只有他保存着私钥。
这样,通信信息就不会被其他人截获了,这依赖于私钥的保密性。
对称加密
共用一个密钥来加解密
使用 SSL/TLS 进行通信的双方需要使用非对称加密方案来通信,但是非对称加密设计了较为复杂的数学算法,在实际通信过程中,计算的代价较高,效率太低,因此,SSL/TLS 实际对消息的加密使用的是对称加密。
对称加密:通信双方共享唯一密钥 k,加解密算法已知,加密方利用密钥 k 加密,解密方利用密钥 k 解密,保密性依赖于密钥 k 的保密性。
为何要采用混合加密
单纯使用对称加密的话,依赖于同一个秘钥,丢了的话就寄了。 在HTTPS的传输场景下,服务端事先并不知道客户端是谁,你也不可能在事先不通过互联网和每一个网站的管理员都悄悄商量好一个通信密钥出来,那么必然存在一个密钥在互联网上传输的过程,如果在传输过程中被别人监听到了,那么后续的所有加密都是无用功。
而非对称加密,又太过复杂。
- 所以需要混合,使用非对称加密,来加密保护【对称加密需要依赖的那个秘钥】,这样就能保证该秘钥绝对安全,之后就可以单纯使用对称加密了。
HTTP 与 HTTPS 有哪些区别?
- HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
- HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。
- 两者的默认端口不一样,HTTP 默认端口号是 80,HTTPS 默认端口号是 443。
- HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的
🎈HTTPS过程中,客户端如何验证证书的?
CA签发证书的过程
生成证书的时候,会把证书里边的信息,用哈希算法加密生成一段密文,然后再用私钥加密这段密文,生成签名,把签名附着在证书上,便于后续客户端来主动校验
浏览器验证
- 客户端先判断证书的有效期,是否过期了
- 浏览器开始查找操作系统中,是否已信任改证书发布机构CA
- 若找不到,则会提示:证书不可信任
- 若找到,则开始验签
验签
- 客户端拿到服务端的证书,取出里边的信息,用相同的哈希算法对该信息加密,得到hashA
- 再对数字签名用公钥解密,得到hashB
- 判断hashA和hashB是否一致,一致则说明没有被篡改过,不一致的话,说明肯定被篡改过。【因为黑客不知道秘钥,即使自行篡改了数据,生成的签名也不对应】
🎈私钥加密公钥解密的用途
由于私钥是服务端存储好的,而公钥是服务端分发给所有想跟他交换的用户
- 若采用私钥加密,则只有拥有公钥的用户,都可以来解密
用途何在?
能确保该消息肯定是服务端发送出来的,因为只有服务端存有秘钥,相当于一对多的关系【广播】
🎈🎈HTTPS通信的全过程
- 【建立通信阶段--非对称加密】先经历SSL步骤【更具体看[[RSA握手过程]]】
- 确认TLS版本,密码套件,两个随机数之后
- 验证服务端证书有效,则用公钥再加密一个随机数传递给服务端
- 服务端用私钥解密,得到这个随机数,此时双方都有了3个随机数,都去加密生成一个【会话秘钥】
- 【通信建立后的阶段】双方都使用这个【会话秘钥】,对称加密,对称解密
🎈HTTPS就是绝对安全的吗?
如果有中间人攻击,一方面伪装成服务端跟真正的客户端通信,另一方面又伪装成客户端跟真正的服务端通信,这时HTTPS就是不安全的。
但本质上并不是因为HTTPS本身不安全,而是用户不顾浏览器的证书风险或操作系统中了病毒,植入了中间人的证书。
首先说一下中间人攻击的过程:
HTTPS通信之前,要经过TLS握手交换会话秘钥,下边以RSA握手为例:
- 客户端生成随机数,并且亮出自己支持的SSL套件,发送给服务端
- 这一步被中间人截取了,中间人生成随机数,从SSL套件里边选择一个加密算法告知客户端,并且把自己的证书发送给客户端
- 客户端开始验证中间人的证书,由于中间人的证书是伪造的,此时若操作系统没有植入中间人的根证书,浏览器是会提醒不安全的证书
另外,如果你的电脑中毒了,被恶意导入了中间人的根证书,那么在验证中间人的证书的时候,由于你操作系统信任了中间人的根证书,那么等同于中间人的证书是合法的,这种情况下,浏览器是不会弹出证书存在问题的风险提醒的。
- 若客户端执意访问,相当于信任了中间人的证书,那么验证证书这一环节就通过了,拿到了中间人的公钥,再生成一个随机数,用公钥加密这个随机数传递给中间人
- 中间人用自己的私钥,解密得到这个随机数,到这一步,客户端和中间人都握有3个随机数,各自用公钥再次加密这3个随机数的组合,便得到了【会话密钥】
注意这个会话秘钥是各自生成的,无需在网络中传递,也就减少了窃听的风险
到这一步,客户端就完全信任了中间人,认为中间人就是服务端了,并且跟中间人建立起了通信,之后客户端发送给服务端的信息,实际上都是传递给中间人
之后就是中间人跟服务端建立联系,伪装自己为客户端,过程跟上边是大同小异的
- 中间人窃取了服务端发送给客户端的数字证书,解析出其中的公钥
- 那后续中间人完全可以跟服务端建立通信了
因为服务端其实是没有校验客户端的,任何客户端,只要拿到证书和其中的公钥,都是可以跟服务端建立连接的
抓包工具是如何实现的?
其实是抓包工具,往我们的操作系统中,植入了ta生成的证书,这样就是实现中间人攻击了,客户端信任中间人的证书。
对于 HTTPS 连接来说,中间人要满足以下两点,才能实现真正的明文代理:
- 中间人,作为客户端与真实服务端建立连接这一步不会有问题,因为服务端不会校验客户端的身份;
- 中间人,作为服务端与真实客户端建立连接,这里会有客户端信任服务端的问题,也就是服务端必须有对应域名的私钥;
中间人要拿到私钥只能通过如下方式:
- 去网站服务端拿到私钥;
- 去CA处拿域名签发私钥;
- 自己签发证书,切要被浏览器信任;
不用解释,抓包工具只能使用第三种方式取得中间人的身份。
使用抓包工具进行 HTTPS 抓包的时候,需要在客户端安装 Fiddler 的根证书,这里实际上起认证中心(CA)的作用。 抓包工具能够抓包的关键是客户端会往系统受信任的根证书列表中导入抓包工具生成的证书,而这个证书会被浏览器信任,也就是抓包工具给自己创建了一个认证中心 CA,客户端拿着中间人签发的证书去中间人自己的 CA 去认证,当然认为这个证书是有效的。
如何避免中间人攻击
可以通过 HTTPS 双向认证来避免这种问题。
一般我们的 HTTPS 是单向认证,客户端只会验证了服务端的身份,但是服务端并不会验证客户端的身份。
如果用了双向认证方式,不仅客户端会验证服务端的身份,而且服务端也会验证客户端的身份。
服务端一旦验证到请求自己的客户端为不可信任的,服务端就拒绝继续通信,客户端如果发现服务端为不可信任的,那么也中止通信。
🍿🍿🍿手撕面答环节 -- 这是一条分割线
🍔HTTPS就是绝对安全的吗?
如果有中间人攻击,一方面伪装成服务端跟真正的客户端通信,另一方面又伪装成客户端跟真正的服务端通信,这时HTTPS就是不安全的,但本质上并不是因为HTTPS本身不安全,而是用户不顾浏览器的证书风险或操作系统中了病毒,植入了中间人的证书。
首先说一下中间人攻击的过程:
HTTPS通信之前,要经过SSL层交换会话秘钥,下边以RSA握手为例:
- 客户端生成随机数,并且亮出自己支持的SSL套件,发送给服务端
- 这一步被中间人截取了,中间人生成随机数,从SSL套件里边选择一个加密算法告知客户端,并且把自己的证书发送给客户端
- 客户端开始验证中间人的证书,由于中间人的证书是伪造的,此时若操作系统没有植入中间人的根证书,浏览器是会提醒不安全的证书
- 若客户端执意访问,相当于信任了中间人的证书,那么验证证书这一环节就通过了,拿到了中间人的公钥,再生成一个随机数,用公钥加密这个随机数传递给中间人
- 中间人用自己的私钥,解密得到这个随机数,到这一步,客户端和中间人都握有3个随机数,各自用公钥再次加密这3个随机数的组合,便得到了【会话密钥】
注意这个会话秘钥是各自生成的,无需在网络中传递,也就减少了窃听的风险
到这一步,客户端就完全信任了中间人,认为中间人就是服务端了,并且跟中间人建立起了通信,之后客户端发送给服务端的信息,实际上都是传递给中间人
之后就是中间人跟服务端建立联系,伪装自己为客户端,过程跟上边是大同小异的
🍔HTTP请求方法你知道多少?
客户端发送的 请求报文 第一行为请求行,包含了方法字段。
根据 HTTP 标准,HTTP 请求可以使用多种请求方法。
HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1 新增了六种请求方法:OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法。
| 序 号 | 方法 | 描述 |
|---|---|---|
| 1 | GET | 请求指定的页面信息,并返回实体主体。 |
| 2 | HEAD | 类似于 GET 请求,只不过返回的响应中没有具体的内容,用于获取报头 |
| 3 | POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件)。数据被包含在请求体中。POST 请求可能会导致新的资源的建立和/或已有资源的修改。 |
| 4 | PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
| 5 | DELETE | 请求服务器删除指定的页面。 |
| 6 | CONNECT | HTTP/1.1 协议中预留给能够将连接改为管道方式的代理服务器。 |
| 7 | OPTIONS | 允许客户端查看服务器的性能。 |
| 8 | TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
| 9 | PATCH | 是对 PUT 方法的补充,用来对已知资源进行局部更新 。 |
🍔Get和Post的区别?
- get一般用于查询等获取数据的请求,post一般是修改请求
- get是幂等的,post有可能是非幂等的,因为涉及到修改
- get的请求是放在url上的,以?和 & 来分隔,不太安全,而post放在 request body 里边,相对更加安全
- get提交的数据最大是2k( 限制实际上取决于浏览器), post理论上没有限制。
- get产生一个TCP数据包,浏览器会把http header和data一并发送出去,服务器响应200(返回数据); post产生两个TCP数据包,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
- get请求会被浏览器主动缓存,post不会,需要手动设置
🍔POST 方法比 GET 方法安全?
看起来确实是这样,因为post的数据在地址栏上不可见。
然而,从传输的角度来说,他们都是不安全的,因为 HTTP 在网络上是明文传输的,只要在网络节点上捉包,就能完整地获取数据报文。
要想安全传输,就只能使用HTTPS密文传输。
🍔说下 HTTP/1.0,1.1,2.0 的区别?
关键需要记住 HTTP/1.0 默认是短连接,可以强制开启,HTTP/1.1 默认长连接,HTTP/2.0 采用多路复用
HTTP/1.0
- 默认使用短连接,每次请求都需要建立一个 TCP 连接。它可以设置
Connection: keep-alive这个字段,强制开启长连接。
HTTP/1.1
- 引入了持久连接,即 TCP 连接默认不关闭,可以被多个请求复用。
- 分块传输编码,即服务端每产生一块数据,就发送一块,用” 流模式” 取代” 缓存模式”。
- 管道机制,即在同一个 TCP 连接里面,客户端可以同时发送多个请求。
HTTP/2.0
- 二进制协议,1.1 版本的头信息是文本(ASCII 编码),数据体可以是文本或者二进制;2.0 中,头信息和数据体都是二进制。
- 完全多路复用,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应。
- 报头压缩,HTTP 协议不带有状态,每次请求都必须附上所有信息。Http/2.0 引入了头信息压缩机制,使用 gzip 或 compress 压缩后再发送。
- 服务端推送,允许服务器未经请求,主动向客户端发送资源。
🍔HTTP/3 了解吗?
HTTP/3 主要有两大变化,传输层基于 UDP、使用QUIC 保证 UDP 可靠性。
HTTP/2 存在的一些问题,比如重传等等,都是由于 TCP 本身的特性导致的,所以 HTTP/3 在 QUIC 的基础上进行发展而来,QUIC(Quick UDP Connections)直译为快速 UDP 网络连接,底层使用 UDP 进行数据传输。
HTTP/3 主要有这些特点:
- 使用 UDP 作为传输层进行通信
- 在 UDP 的基础上 QUIC 协议保证了 HTTP/3 的安全性,在传输的过程中就完成了 TLS 加密握手
- HTTPS 要建⽴⼀个连接,要花费 6 次交互,先是建⽴三次握⼿,然后是 TLS/1.3 的三次握手。QUIC 直接把以往的 TCP 和 TLS/1.3 的 6 次交互合并成了 3 次,减少了交互次数。
- QUIC 有自己的一套机制可以保证传输的可靠性的。当某个流发⽣丢包时,只会阻塞这个流,其他流不会受到影响。
参考
- 湖科大教书匠计算机网络视频、
- 小林coding