HTTP
HTTP常见的面试题
HTTP基本概念
HTTP是什么
HTTP是超文本传输协议,即就是HyperText Transfer Protocol
可以拆开解释:
- 协议:指两个或两个以上的对象达成一种行为的约定和规范
- 传输:HTTP是双向协议且可以在在中间有中转或接力。
- 超文本:图片,文字,视频等的混合体。
一句话解释就是,HTTP是一个在计算机世界里专门在两点之间传输文字,图片,视频等的超文本信息的约定和规范。
HTTP常见的状态码
五大类常见的状态码
- 1XX :提示信息
- 2XX:成功
- 3XX:重定向
- 4xx: 客户端错误
- 5XX:服务器错误
HTTP常见的字段
- HOST字段:用于指定服务器的域名。
- Content- length 字段:服务器本次回应的数据长度。
- Connection字段:客户端要求服务器使用HTTP长连接机制。
- Content-Type 字段:用于服务器回应数据格式。
- Content-Encoding字段:用于服务器回应数据的压缩格式。
GET与 POST
GET与POST的区别
- GET:语义是从服务器获取指定的资源,请求的参数位置一般写在URL中。
- POST:语义是根据请求符合(报文body)对指定资源做处理,
这两种方法都是安全和幂等的吗
- 安全:就是指方法不破坏服务器上的资源。
- 幂等:就是多次执行相同的操作,结果相同。
从RFC规范定义的语义分析来看:GET是安全且幂等的(只读),而POST是不安全且不幂等的(会修改和新增)。
但实际应用中,GET去新增或删除数据就是不安全不幂等的,POST刚好也是相反。
- 理论上,任何请求都能带BODY,但规范里不需要。
HTTP缓存技术
缓存实现方式
缓存是将重复性的请求响应缓存在本地,实现方式有两种:强制缓存和协商缓存。
- 强制缓存:决定是否使用缓存的主动性在浏览器这边,HTTP响应头部有两字段– Cache-Control(相对时间)和Expires(绝对时间)
,且Cache-Control优先级高于Expires,因此优先使用前者,具体实现是:第一次访问在response加上设置了时间的字段—再次访问时,计算时间是否
过期–服务器收到请求后更新字段。 - 协商缓存:通过服务端来告知客户端是否可以使用缓存的方式就是协商缓存;具体实现是靠两种头部的字段实现,
协商缓存这两个字段都需要配合Cache-Control字段来使用,且只有未命中强制缓存的时候,才能发起带有协商缓存字段的请求。
HTTP特性
目前HTTP版本不少,有1.0,2.0,3.0,以1.0为例:
HTTP1.0的优点
- 简单:文本形式简单。具体是:
- 灵活和易于扩展:允许自定义和扩充,下层可随意变化
- 应用广泛和跨平台
HTTP1.0缺点
- 无状态双刃剑:服务器可以不记忆HTTP记忆状态,但完成有关联性操作时会非常麻烦。
- 明文传输双刃剑:调试工作方便,但信息裸奔,容易被窃取。
- 不安全:1.通信使用明文。2.不验证对方身份。3.无法证明报文完整性。
HTTP1.1性能如何
- 长连接:比起1.0的每次三次握手,1.1变成了长连接。
- 管道网传输:同一个TCP连接里,多个请求,第一个请求发出后可不等返回,这样减少整体的响应时间。
- 队头阻塞:1.1的管道解决了请求的队头阻塞,但没解决响应的队头阻塞。队头阻塞就是第一个请求阻塞会导致后面所有请求都阻塞。
HTTP和HTTPS
两者区别
- HTTPS解决了HTTP不安全的缺陷,再TCP和HTTP网络层中加入了SSL/TLS安全协议。
- 相较于HTTP,多了一部SSL/TLS的握手过程。
- 默认端口不同,HTTP默认为80,HTTPS默认为443。
- 此外,HTTPS还需要向CA申请数字证书,来保证服务器身份可信。
HTTPS解决了哪些问题
- 窃听风险
- 篡改风险
- 冒充风险
如何解决
- 信息加密
- 校验机制
- 身份证书
具体办法:
- 混合加密 — 实现信息的机密性
– 对称加密和非对称加密的混合,通信建立前非对称,通信过程对称,对称加密运算速度快,但无法安全进行密钥交换,非对称刚好相反。 - 摘要算法 — 实现完整性,能够生成独一无二的“指纹”,用哈希函数计算内容的哈希值(指纹),通过哈希验证才能篡改,但
不能保证哈希+内容一并被换,因为缺少客户端的消息是否来源于服务器证明。解决办法是用非对称加密算法,即使用公私密钥,
公钥加密私钥开 – 保证了内容传输安全,内容只有私钥才能解开
私钥加密公钥开 – 保证消息不被冒充,这就是非对称加密。 - 将服务器公钥放入数字证书中。为了解决身份验证环节,即公钥是否是对应身份的。
服务器将自己的公钥注册到CA中,CA用自己的私钥将服务器公钥数字签名并颁发证书,客户端会使用CA公钥确认服务器的数字证书真实性,然后获取公钥解密。
HTTPS如何建立连接
SSL/TLS协议基本流程:
- 客户端向服务器索要并验证服务器密钥
- 双方协商生产会话密钥
- 双方采取会话密钥进行加密通信
客户端校验数字证书流程如何
- CA签发证书过程 : CA将持有者的有效信息打包并hash计算 — CA使用自己的私钥加密即签名 — 最后将CA添加到文件证书上。
- 客户端校验过程 : 客户端用hash计算证书hash值 — 浏览器使用CA公钥解密证书内容即hash值 — 比较两hash值是否相等。
除此之外,证书还存在证书信任链的问题,即我们向CA申请的证书不是根证书,而是中间证书签发。
这样在校验时会根据中间证书签发者向上寻求根证书。这样设置是为了保证根证书的绝对安全性。
HTTPS的应用数据如何保证完整性
TLS上主要实现握手协议和记录协议:
- 握手协议主要实现 协商加密算法和生成对称密钥
- 记录协议负责验证并保证数据的完整性和来源。
具体实现:
- 消息分段并压缩 — 加入消息认证码 — 通过对称密码加密 — 加入数据类型等报头组成最终的报文数据。
HTTPS一定可靠吗
场景题:客户端与服务端之间多了中间人服务器,这样还能说明HTTPS安全可靠吗
- 实际上中间人返回给客户端的证书是伪造的,浏览器可以识别并报警,如果用户点击信任此中间人,就会建立往下的连接。
- 或者电脑中病毒,中间人的证书被根植在电脑里
所以:HTTPS协议本身没有任何漏洞,即使成功进行了中间人攻击,本质也是利用客户端的漏洞,并非HTTPS不安全。
- 为什么抓包工具能截取HTTPS数据呢?
- 与中间人原理一致:中间人既需要与真实服务器连接不被发现,又需要与客户端连接不被怀疑,与服务器之间不存在问题,服务器不校验客户端身份
但客户端会校验,因此需要对应域名的私钥。 - 如何获取私钥?
- 去网站服务端拿 2. 去CA认证处拿域名签发私钥 3. 自己签发证书,且要被浏览器信任。
- 当然只能通过第三种实现,也就是自己创建了CA。
- 如何避免被抓取数据?
- 建立HTTPS双向认证,即服务端也会验证客户端身份。
HTTP的演进
- HTTP/1.0 — HTTP/1.1 : 1. 使用长连接 2. 支持管道网络传输
- HTTP/1.1 — HTTP/2 : 1. 头部压缩 (HPACK)将字段存入两端共同维护的表格,然后只发送索引号。2. 二进制格式,头信息和数据体统称为桢。3.
并发传输,解决队头阻塞,引入stream 概念,不同HTTP请求有独有的streamid来区分,可以乱序发送。4.服务器推送,两端都可以建立stream。 - HTTP/2 — HTTP/3 : 1.解决2.0遗漏的阻塞问题,将tcp改为udp,使用QUIC协议—好处:i.无队头阻塞,某个流丢失时,只会阻塞该流。ii. 更快的连接建立,iii.连接迁移。
HTTP/1.1 如何优化
有三种优化思路:1.尽量避免发送请求 2.在需要发送请求时,考虑减少请求次数。3.减少服务器的HTTP响应数据大小。
尽量避免发送请求
意思是,对于重复性的请求缓存在本地,用到缓存技术,如何做到:
- 客户端第一次的请求以及响应数据保存在本地,并设置过期时间,当再次访问同样的请求时,可以直接从本地获取,当然如果过了时间,就会将此资源的摘要带在请求头部中
,服务端会与自身进行对比,相同则可以用,仅仅返回一个不含包体得到响应304。
如何减少HTTP请求
有三个方面:1.减少重定向请求次数 2.合并请求 3. 延迟发送请求
减少重定向请求次数
重定向是指服务器的资源从url1 迁移到url2,但客户端不知情。
- 解决办法:重定向工作1交由代理服务器完成。返回302重定向响应码(临时重定向)301(永久)308(类似永久)。
合并请求
为了防止阻塞,会发送多个请求,将多个小请求合并成大请求,例如小资源合并为大资源一起请求回来,
但是缺陷是一旦资源丢失,就要重新下载。
延迟请求
— 按需获取
如何减少HTTP响应的数据大小
最常用的方式是压缩,压缩又分为有损压缩和无损压缩:
- 无损压缩:GZIP最常见的无损压缩
- 有损压缩:请求头中会有质量因子,告诉服务器期望资源质量。
HTTPS RSA 握手解析
不同的密钥交换算法,TLS握手就不同,以RSA为例:
第一次握手
客户端首先发送Client hello 消息,里面包含客户端的TLS版本号,支持的密码套件列表,以及随机数。
第二次握手
服务端收到消息后,返回 Server hello 消息,里面包含服务端TLS版本号,随机数,以及合适的密码套件(基本形式:
密钥交换算法 + 签名算法 + 通信使用的对称加密算法 + 摘要算法),然后发送Server Certificate 包含数字证书,最后发送Server Hello Done 结束本次招呼
第三次握手
客户端验证完证书后,生成新的随机数并用服务器RSA公钥加密,发送 Client key Exchange 给服务端,至此两端都共享了三个随机数,然后生成
会话密钥,发送Change cipher Spec ,然后再发送 Encrypted Handshake Message (Finished) ,把之前发送数据做个摘要并用会话密钥加密
第四次握手
服务端也是同样的操作,双方加密解密没问题的话,握手正式完成。
RSA算法最大的缺陷
使用RSA密钥协商算法最大问题是不支持前向保密。服务端私钥泄露就会造成密文被破解。
HTTPS ECDHE握手分析
基于离散对数设计的,知道对数求真数好求,知道真数求对数几乎不可能实现。
基于此设计了DH算法,其有两种实现,静态DH算法和DHE算法,DHE算法计算性能不佳,要做大量乘法,改进为ECDHE 算法
ECDHE算法
基本流程 - 双方确定椭圆曲线和曲线基点,各自准备随机数做私钥并与几点相乘得到公钥,交换公钥后根据椭圆曲线可以计算出会话密钥。
握手过程
第一次握手
客户端发送请求信息,包含TLS版本号,支持的密码套件列表,以及随机数。
第二次握手
服务端收到消息,返回确认信息,包含版本号,随机数,以及密码套件(密钥协商算法 + 签名算法 + 通信使用的对称加密算法 + 摘要算法);
然后发送验证信息,包含证书;随后与RAS算法有区别,发送Server Key Exchange消息。最后发送消息提供完毕通知。
这一整个过程干的三件事有:
- 服务端选择了椭圆曲线,同时曲线基点也就已知了
- 生成了随机数
- 根据基点和私钥计算服务端的公钥
第三次握手
客户端验证证书合法后,会生成随机数用作客户端的私钥,然后根据发送过来的信息计算公钥并发送给服务端,至此,双方都可以计算出ECDHE算法算出的共享密钥
,然后双方都有了会话密钥(客户端随机数 + 服务端随机数 + 共享密钥),然后客户端发送更改算法请求,接着发送之前数据的摘要并用对称密钥加密,
让服务端验证对称密钥是否可用。
第四次握手
服务端同样的操作。
HTTPS如何优化
产生损耗的环节在:
- TLS握手过程
- 握手后的对称加密报文传输
硬件优化
因为HTTPS协议是计算密集型,所以可以提升cpu,另外可以选择支持AES-NI特性的CPU,这样可以加速数据的加解密传输过程。
软件优化
分两种:软件升级 和 协议优化
软件升级
软件升级成本过高,可能影响项目上线等。
协议优化
就是对密钥交换过程进行优化。
密钥交换算法优化
RSA交换算法花费2RTT且不具备安全性,所以尽量选择ECDHE交换算法,花费1RTT且安全性也高。
对称加密算法方面,可以选用AES_128_GCM,能比256快一些。
TLS升级
可以直接把TLS1.2升级到TLS1.3,这样完成TLS握手只需要1RTT,且安全性也提高了。
之所以减少到了一次,是因为1.3把hello和公钥交换合并在一起了,于是就减少到只需要1RTT就能握手。
除此之外对于密钥交换算法,废除了不支持前向安全性的RSA和DH算法,只支持ECDHE算法。
证书优化
两个方向:
- 证书传输
- 证书验证
证书传输优化
优先选择 椭圆曲线(ECDSA)证书,因为相同安全强度下,ECC密钥长度比RSA短很多。
证书验证优化
验证的方法:
- CRL : 证书吊销列表,客户端下载证书吊销列表并遍历查找。带来的问题就是1.证书刚被吊销后,客户端更新CRL之前仍信任,实时性较差,2.证书越多,下载越慢,拖慢连接。
- OCSP : 在线证书状态协议,工作方式是客户端向CA发送查询请求,让CA返回证书有效状态。但还有问题就是:查询时会有网络开销。
- OCSP Stapling:
为了解决这一网络开销,出现了OCSP Stapling 原理是服务器定期查询,获得带有时间戳和签名的响应并缓存,然后建立连接时发送给客户端,这样客户端就不需要查询了。
会话复用
目的是缓存加密密钥,然后下次连接时直接复用,这样就减少了性能损耗。
分为两种:
- Session ID
- Session Ticket
Session ID
两端口首次连接后,双方都会缓存会话密钥,并用唯一Session ID标识。
但存在两个缺点:
- 客户端增多,服务器内存压力增大。
- 服务器负载均衡,客户端再次连接不一定命中上次访问过的服务器。
Session Ticket
服务器不存了,将缓存工作交给客户端,下次连接时客户端将缓存发送给服务端解密。对于集群服务器,确保每台服务器密钥一致。
而这两种都不具备前向安全性,应对重放攻击很难。重放攻击是指第三者窃听了对话并保留了密码,这样就可以冒充并删改数据了。
避免重放攻击的方式是对会话密钥设计一个合理的过期时间。
Pre-shared Key
在TLS1.3中,客户端会把Ticket一同发送给服务端。同样也有重放攻击的危险,处理方式也是设定合理过期时间。
HTTP/2
HTTP1.1因为传输资源变大变多变杂了,实时性要求也高了,所以带来了最大的问题是:高延迟。
高延迟的主要原因是:1.延迟已达下限,难以下降。2.并发连接有限。3. 队头阻塞问题。4.HTTP头部巨大且重复。5.不支持服务器推送消息。
针对HTTP1.1的优化已经讲过,但一些关键地方无法优化,比如请求响应模型。头部巨大重复,并发连接耗时等等。
兼容HTTP1.1
HTTP2兼容1.1,主要是其只在语法层面做了改造,语义层面没做改动,也就是说改了格式但意思没变。
头部压缩
头部含有固定的字段且大量请求和响应报文内的字段重复,因此有必要进行压缩,但2.0不采用gzip压缩,而是HPACK算法,
HPACK算法包含三个部分:
- 静态字典
- 动态字典
- HUFFMAN编码
静态字典
是为高频出现的头部字符串和字段建立一张静态表,其中的header Value 值是使用Huffman编码了,这样可以将压缩率达到47%。
动态编码表
第一次发送后。双方更新自己的动态表,但动态表生效的前提是 必须在同一连接上,重复传输完全相同的HTTP头部。
二进制桢
2将响应报文划分为两类桢:HEADERS(首部) 和 DATA(消息负载)
桢头结构内首先是一个帧长度,然后后面跟着桢类型(数据帧和控制桢),桢类型后面是一个标志位(用于携带简单的控制信息),
桢头后四位是流标识符,用于标识Frame是哪个Stream 的。
最后是帧数据,存放通过HPACK算法压缩过的HTTP头部和包体。
并发传输
多个Stream 跑在同一条TCP连接中,同一个HTTP请求与响应跑在同一个Stream中,HTTP消息可以由很多个Frame构成,一个Frame可以由多个TCP报文构成。
且双方都可以建立Stream,同时可以在帧头标志位设置优先级。
服务器主动推送资源
客户端发送的请求必须使用奇数号Stream,而服务器主动的推送使用的是偶数号。这样的区分是一种协议约定,用于帮助维护流之间的清晰组织和管理。
HTTP/3
HTTP/2的不足
- 队头阻塞:因为多个请求是在一个TCP连接中的,所以当TCP丢包时,整个TCP都要等待重传,从而阻塞所有请求。
- TCP与TLS的握手延迟:一是建立连接总共需要3RTT,二是刚建立连接的TCP会有慢启动的过程。
- 网络迁移需要重新连接:TCP连接由四元组(源IP地址,源端口,目标IP地址,目标端口)确定的。因此一旦切换场景就需要重新握手。
解决办法是:将传输层协议替换成UDP
QUIC协议特点
UDP包之间无序且无依赖关系,UDP不需要连接,自然比TCP快。
HTTP/3 不仅仅简单的将传输协议替换成UDP,还基于UDP协议在应用层实现QUIC协议,具备类似TCP的连接管理,拥塞窗口,流量控制的网络特性。
QUIC的优点:
- 无队头阻塞:也有类似strem与多路复用的概念,但因为无序,所以数据包丢失UDP不关心。但为了保证数据包的可靠性,每条流的每个数据包都由唯一标识序号,有包丢了,数据也无法被
获取,直到重传丢失报文,其他流是不受影响,这是与HTTP2不同的地方。 - 更快的连接建立:QUIC内部包含TLS,自己的桢会携带TLS里的记录,再者,QUIC使用的是TLS1.3,因此仅需1RTT,甚至第二次连接时,数据包可和握手信息一并发送,达到0-RTT
- 连接迁移:QUIC协议通过连接ID标记通信两端,即便ip地址发生变化,只要仍保有上下文信息,就可以。
HTTP/3协议
同2一样,采用二进制桢结构,但是3的桢结构很简单,只有两个字段:类型和长度。桢类型大体分为数据帧和控制桢。
3在头部压缩算法这方面也做了升级,升级成为QPACK,同样采用静态表,动态表和Huffman 编码。但有所改变:
- 静态表:由原来的61项扩展到91项。
- 动态表编解码方式不同:动态表具有时许性,当首次出现的请求发生丢包时,后续收到的请求,对方无法解码出HPACK头部,因为对方没建立好动态表,这样后续的请求就阻塞到首次请求中丢失的数据包重传。
3中如何解决这一问题:QUIC会有两个特殊的单向流:
- 一个是QPACK Encoder Stream,用于将一个字典传给对方,当面对不属于静态表时会发送。
- 一个是 QPACK Decoder Stream ,用于响应对方,告诉对方自己刚发的字典已经更新到自己本地的动态表了。
这两个流时用来同步双方的动态表的,当收到更新确认通知后才使用动态表编码HTTP头部。
RPC协议存在的必要性
TCP有三个特点,面向连接,可靠和基于字节流。如果使用纯裸的TCP,就会造成数据没有边界,及就是粘包问题。所以
要在此基础上,加入一些自定义的规则,于是就衍生出了像 HTTP 和 RPC等众多协议。
HTTP 和 RPC
RPC是远程过程调用,本身不是一个具体的协议,而是调用方式,其底层可以使用TCP,也可以使用UDP或者HTTP:
RPC 和 HTTP 的选择
HTTP是 Browser / Server 的协议 ,是需要一套标准的。
RPC是 Client / Server 的协议,所以只管连到自家公司就ok。
两者的区别:
- 服务发现: HTTP是通过DNS服务解析的,而RPC是有专门的中间服务去保存服务名和IP地址。两者差别不大
- 底层连接形式:都是建立TCP长连接进行数据交互,但是RPC不同的是:它会再建立一个连接池,发送数据时从池里取出连接,用完后放回,下次复用。
- 传输内容: 两者都是将二进制数据进行序列化,只是方案不同,比如HTTP是JSON,RPC是Protobuf。相比之下,Protobuf的体积更小,定制化成度更高。
因此公司内部更倾向于选择RPC。
HTTP2.0 是2015年出现的,这时候很多公司内部已经使用RPC很多年了,所以没必要更换。
WebSocket
如何做到用户在不做任何操作的情况下,网页收到消息并发生变更:
- 使用HTTP不断轮询: 前端代码不断轮询,常见的是微信扫码,轮询间隔一般为1-2秒,最坏的结果是用户等待1-2秒,会有明显的卡顿。
- 长轮询: 将超时设置为30秒,超时了就立马发起下一次请求。
websocket介绍
TCP连接的两端可以是全双工,但是HTTP1.1协议里,变成了半双工。
为了支持客户端和服务器端之间互相发送大量数据的场景,WebSocket协议被设计出来了。
如何建立WebSocket连接
先统一使用HTTP协议进行一次通信,普通HTTP请求则继续使用,如果想建立WebSocket连接,则在请求头上带些特殊的头,
这些头意思是想升级协议,并携带随机生成的base64码,浏览器验证base64码后验证通过。
所以websocket协议只是借助http完成升级,并不是以http为基础。