一、HTTP 基本概念
1.1 HTTP 是什么?
HTTP(超文本传输协议)是一种 应用层协议 ,主要用于在Web上进行数据通信。
HTTP的核心功能是 规范客户端和服务端之间的交互过程 ,包括请求和响应的消息格式、方法和状态码等。这种设计使得HTTP成为万维网的基础协议,实现了网页、图像等资源的有效传输和呈现。
1.2 HTTP 常见的状态码有哪些?
1xx
类状态码属于提示信息,是协议处理中的一种中间状态,实际用到的比较少。
2xx
类状态码表示服务器成功处理了客户端的请求,也是我们最愿意看到的状态。
「200 OK」是最常见的成功状态码,表示一切正常。如果是非
HEAD
请求,服务器返回的响应头都会有 body 数据。「204 No Content」也是常见的成功状态码,与 200 OK 基本相同,但响应头没有 body 数据。
「206 Partial Content」是应用于 HTTP 分块下载或断点续传,表示响应返回的 body 数据并不是资源的全部,而是其中的一部分,也是服务器处理成功的状态。
3xx
类状态码表示客户端请求的资源发生了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向。
「301 Moved Permanently」表示永久重定向,说明请求的资源已经不存在了,需改用新的 URL 再次访问。
「302 Found」表示临时重定向,说明请求的资源还在,但暂时需要用另一个 URL 来访问。
301 和 302 都会在响应头里使用字段 Location
,指明后续要跳转的 URL,浏览器会自动重定向新的 URL。
4xx
类状态码表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。
「400 Bad Request」表示客户端请求的报文有错误,但只是个笼统的错误。
「403 Forbidden」表示服务器禁止访问资源,并不是客户端的请求出错。
「404 Not Found」表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。
5xx
类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。
「500 Internal Server Error」与 400 类型,是个笼统通用的错误码,服务器发生了什么错误,我们并不知道。
「501 Not Implemented」表示客户端请求的功能还不支持,类似“即将开业,敬请期待”的意思。
「502 Bad Gateway」通常是服务器作为网关或代理时返回的错误码,表示服务器自身工作正常,访问后端服务器发生了错误。
「503 Service Unavailable」表示服务器当前很忙,暂时无法响应客户端,类似“网络服务正忙,请稍后重试”的意思。
1.3 HTTP 常见字段有哪些?
Host 字段
客户端发送请求时,用来指定服务器的域名。
Content-Length 字段
服务器在返回数据时,会有 Content-Length
字段,表明本次回应的数据长度。
如上面则是告诉浏览器,本次服务器回应的数据长度是 1000 个字节,后面的字节就属于下一个回应了。
大家应该都知道 HTTP 是基于 TCP 传输协议进行通信的,而使用了 TCP 传输协议,就会存在一个“粘包”的问题,HTTP 协议通过设置回车符、换行符作为 HTTP header 的边界,通过 Content-Length 字段作为 HTTP body 的边界,这两个方式都是为了解决“粘包”的问题。
Connection 字段
Connection
字段最常用于客户端要求服务器使用「HTTP 长连接」机制,以便其他请求复用。
HTTP 长连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。
HTTP/1.1 版本的默认连接都是长连接,但为了兼容老版本的 HTTP,需要指定 Connection
首部字段的值为 Keep-Alive
。
Connection: Keep-Alive
开启了 HTTP Keep-Alive 机制后, 连接就不会中断,而是保持连接。
当客户端发送另一个请求时,它会使用同一个连接,一直持续到客户端或服务器端提出断开连接。
Content-Type 字段
Content-Type
字段用于服务器回应时,告诉客户端,本次数据是什么格式。
Content-Type: text/html; Charset=utf-8
上面的类型表明,发送的是网页,而且编码是UTF-8。
客户端请求的时候,可以使用 Accept
字段声明自己可以接受哪些数据格式。
Accept: */*
上面代码中,客户端声明自己可以接受任何格式的数据。
Content-Encoding 字段
Content-Encoding
字段说明数据的压缩方法。表示服务器返回的数据使用了什么压缩格式
Content-Encoding: gzip
上面表示服务器返回的数据采用了 gzip 方式压缩,告知客户端需要用此方式解压。
客户端在请求时,用 Accept-Encoding
字段说明自己可以接受哪些压缩方法。
Accept-Encoding: gzip, deflate
1.4 HTTP/1.1 版本新特性
相较于HTTP/1.0,HTTP/1.1版本有以下优化
长连接:只要客户端服务端任意一端没有明确提出断开TCP连接,就一直保持连接,可以发送多次HTTP请求
管道网络传输:客户端可以同时发出多个HTTP请求,而不用一个个等待响应
二、GET 与 POST
2.1 GET 和 POST 有什么区别?
根据RFC规范,GET的语义是从服务器获取指定的资源,而POST的语义是对指定的资源做出处理
GET 请求的参数位置一般是写在 URL 中,URL 规定只能支持 ASCII,而且浏览器会对 URL 的长度有限制(HTTP协议本身对 URL长度并没有做任何规定);POST 请求携带数据的位置一般是写在报文 body 中,body 中的数据可以是任意格式的数据,只要客户端与服务端协商好即可,而且浏览器不会对 body 大小做限制
GET请求是幂等的,多次请求相同参数的结果相同,而POST请求用于修改服务器资源,不是幂等的
2.2 GET 和 POST 方法都是安全和幂等的吗?
先说明下安全和幂等的概念:
在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。
所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的。
如果从 RFC 规范定义的语义来看:
GET 方法就是安全且幂等的,因为它是「只读」操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。所以,可以对 GET 请求的数据做缓存,这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如nginx),而且在浏览器中 GET 请求可以保存为书签。
POST 因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。所以,浏览器一般不会缓存 POST 请求,也不能把 POST 请求保存为书签。
三、HTTP 与 HTTPS
3.1 HTTP 与 HTTPS 有哪些区别?
HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。
HTTP 默认端口号是 80,HTTPS 默认端口号是 443。
HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。
3.2 HTTPS 解决了 HTTP 的哪些问题?
HTTP 由于是明文传输,所以安全上存在以下三个风险:
窃听风险,比如通信链路上可以获取通信内容,用户号容易没。
篡改风险,比如强制植入垃圾广告,视觉污染,用户眼容易瞎。
冒充风险,比如冒充淘宝网站,用户钱容易没。
HTTPS 在 HTTP 与 TCP 层之间加入了 SSL/TLS
协议,可以很好的解决了上述的风险:
混合加密的方式实现信息的机密性,解决了窃听的风险。
摘要算法的方式来实现完整性,它能够为数据生成独一无二的「指纹」,指纹用于校验数据的完整性,解决了篡改的风险。
将服务器公钥放入到数字证书中,解决了冒充的风险。
3.2.1 混合加密
对称加密:只使用一个密钥,运算速度快,密钥必须保密,无法做到安全的密钥交换
非对称加密:使用两个密钥:公钥和私钥,公钥可以任意分发而私钥保密,解决了密钥交换问题但速度慢
下面是非对称加密中消息的传输过程:
由于纯对称加密并不安全,而非对称加密虽然安全但是效率太低,因此HTTPS 采用的是对称加密和非对称加密结合的「混合加密」方式:
在通信建立前采用非对称加密的方式交换「会话秘钥」,后续就不再使用非对称加密。
在通信过程中全部使用对称加密的「会话秘钥」的方式加密明文数据。
简而言之为,先使用非对称加密来获取对称加密的密钥,然后再用对称加密来加密明文数据进行传输
3.2.2 摘要算法 + 数字签名
摘要算法:是一种哈希函数,可以保证结果的唯一性,且无法通过哈希值逆推
数字签名:采用非对称加密算法,共有两个密钥,这两个密钥可以双向加解密的,比如可以用公钥加密内容,然后用私钥解密,也可以用私钥加密内容,公钥解密内容。一般用私钥来加密摘要算法得到的哈希值(也称数字签名),然后再用公钥来解密哈希值(数字签名)
数字签名的校验流程如下:
3.2.3 数字证书
尽管数据经过加密,避免了明文传输,但这并不意味着信息安全得到了保障。事实上,HTTP还有一个缺点,还缺少身份验证的环节,万一公钥是被伪造的呢?
假设A和B正在通信,在第一次交流时中间人截获了A的公钥。即使中间人无法使用私钥解密该信息,他也可以采取欺骗手段——冒充A,并将自己生成的公钥发送给B。这样一来,B就会错误地以为那确实是由A发出的信息,并使用中间人的公钥进行加密传输对称加密的密钥。
这时候中间人就可以用自己的私钥解密该信息从而获取对称加密的密钥。接下来他又可以继续搞破坏——伪装成B并再次对该信息进行加密后发送给A,此时A也会认为那确实是由B发出的消息。
通过这种方式,中间人能够在不被双方察觉到的情况下轻松掌握双方之间通信所需用到的对称加密密码,并进而窃取其通信内容。
既然伪造公私钥那么随意,所以A把他的公钥注册到CA (数字证书认证机构),CA用他们自己的私钥对A的公钥做了个数字签名,然后把A的「个人信息 + 公钥 + 数字签名」打包成一个数字证书,也就是说这个数字证书包含A的公钥。
这样,当A和B通信时,B需要先去CA验证数字证书是否合法(此步骤还包含了数字签名的判断,参考2.摘要算法+数字签名),B会拿到CA的公钥进行解密(通常浏览器和操作系统中集成了 CA 的公钥信息),若能成功解密,就能证明证书是可信的,也就能证明A的公钥是可信的
数字证书的工作流程如下:
但事实上,证书的验证过程中还存在一个证书信任链的问题,因为我们向 CA 申请的证书一般不是根证书签发的,而是由中间证书签发的,比如百度的证书,从下图你可以看到,证书的层级有三级:
对于这种三级层级关系的证书的验证过程如下:
客户端收到 baidu.com 的证书后,发现这个证书的签发者不是根证书,就无法根据本地已有的根证书中的公钥去验证 baidu.com 证书是否可信。于是,客户端根据 baidu.com 证书中的签发者,找到该证书的颁发机构是 “GlobalSign Organization Validation CA - SHA256 - G2”,然后向 CA 请求该中间证书。
请求到证书后发现 “GlobalSign Organization Validation CA - SHA256 - G2” 证书是由 “GlobalSign Root CA” 签发的,由于 “GlobalSign Root CA” 没有再上级签发机构,说明它是根证书,也就是自签证书。应用软件会检查此证书有否已预载于根证书清单上,如果有,则可以利用根证书中的公钥去验证 “GlobalSign Organization Validation CA - SHA256 - G2” 证书,如果发现验证通过,就认为该中间证书是可信的。
“GlobalSign Organization Validation CA - SHA256 - G2” 证书被信任后,可以使用 “GlobalSign Organization Validation CA - SHA256 - G2” 证书中的公钥去验证 baidu.com 证书的可信性,如果验证通过,就可以信任 baidu.com 证书。
在这四个步骤中,最开始客户端只信任根证书 GlobalSign Root CA 证书的,然后 “GlobalSign Root CA” 证书信任 “GlobalSign Organization Validation CA - SHA256 - G2” 证书,而 “GlobalSign Organization Validation CA - SHA256 - G2” 证书又信任 baidu.com 证书,于是客户端也信任 baidu.com 证书。
总括来说,由于用户信任 GlobalSign,所以由 GlobalSign 所担保的 baidu.com 可以被信任,另外由于用户信任操作系统或浏览器的软件商,所以由软件商预载了根证书的 GlobalSign 都可被信任。
操作系统里一般都会内置一些根证书,比如我的 MAC 电脑里内置的根证书有这么多:
最后一个问题,为什么需要证书链这么麻烦的流程?Root CA 为什么不直接颁发证书,而是要搞那么多中间层级呢?
这是为了确保根证书的绝对安全性,将根证书隔离地越严格越好,不然根证书如果失守了,那么整个信任链都会有问题。
3.3 HTTPS 是如何建立连接的?其间交互了什么?
SSL/TLS 协议基本流程:
客户端向服务器索要并验证服务器的公钥(非对称)
双方协商生产「会话秘钥」(非对称)
双方采用「会话秘钥」进行加密通信(对称)
前两步也就是 SSL/TLS 的建立过程,也就是 TLS 握手阶段。
TLS的【握手阶段】设计四次通信,使用不同的密钥交换算法,握手流程也会不一样。
常见的密钥交换算法有两种:RSA算法 和 ECDHE算法
3.3.1 RSA 握手过程
传统的 TLS 握手基本都是使用 RSA 算法来实现密钥交换的
详细流程:
1. ClientHello
首先,由客户端向服务器发起加密通信请求,也就是 ClientHello
请求。
在这一步,客户端主要向服务器发送以下信息:
(1)客户端支持的 TLS 协议版本,如 TLS 1.2 版本。
(2)客户端生产的随机数(Client Random
),后面用于生成「会话秘钥」条件之一。
(3)客户端支持的密码套件列表,如 RSA 加密算法。
2. SeverHello
服务器收到客户端请求后,向客户端发出响应,也就是 SeverHello
。服务器回应的内容有如下内容:
(1)确认 TLS 协议版本,如果浏览器不支持,则关闭加密通信。
(2)服务器生产的随机数(Server Random
),也是后面用于生产「会话秘钥」条件之一。
(3)确认的密码套件列表,如 RSA 加密算法。
(4)服务器的数字证书。
可以看到,服务端选择的密码套件是 “Cipher Suite: TLS_RSA_WITH_AES_128_GCM_SHA256”。
这个密码套件看起来真让人头晕,好一大串,但是其实它是有固定格式和规范的。基本的形式是「密钥交换算法 + 签名算法 + 对称加密算法 + 摘要算法」, 一般 WITH 单词前面有两个单词,第一个单词是约定密钥交换的算法,第二个单词是约定证书的验证算法。比如刚才的密码套件的意思就是:
由于 WITH 单词只有一个 RSA,则说明握手时密钥交换算法和签名算法都是使用 RSA;
握手后的通信使用 AES 对称算法,密钥长度 128 位,分组模式是 GCM;
摘要算法 SHA256 用于消息认证和产生随机数;
就前面这两个客户端和服务端相互「打招呼」的过程,客户端和服务端就已确认了 TLS 版本和使用的密码套件
3.客户端回应
客户端收到服务器的回应之后,首先通过浏览器或者操作系统中的 CA 公钥,确认服务器的数字证书的真实性。
如果证书没有问题,客户端会从数字证书中取出服务器的公钥,然后使用它加密报文,向服务器发送如下信息:
(1)一个随机数(pre-master key
)。该随机数会被服务器公钥加密。
(2)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(3)客户端握手结束通知,表示客户端的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供服务端校验。
上面第一项的随机数是整个握手阶段的第三个随机数,会发给服务端,所以这个随机数客户端和服务端都是一样的。
服务器和客户端有了这三个随机数(Client Random、Server Random、pre-master key),接着就用双方协商的加密算法,各自生成本次通信的「会话秘钥」。
4. 服务器的最后回应
服务器收到客户端的第三个随机数(pre-master key
)之后,通过协商的加密算法,计算出本次通信的「会话秘钥」。
然后,向客户端发送最后的信息:
(1)加密通信算法改变通知,表示随后的信息都将用「会话秘钥」加密通信。
(2)服务器握手结束通知,表示服务器的握手阶段已经结束。这一项同时把之前所有内容的发生的数据做个摘要,用来供客户端校验。
至此,整个 TLS 的握手阶段全部结束。接下来,客户端与服务器进入加密通信,就完全是使用普通的 HTTP 协议,只不过用「会话秘钥」加密内容。
3.3.2 RSA 算法的缺陷
使用 RSA 密钥协商算法的最大问题是不支持前向保密。
因为客户端传递随机数(用于生成对称加密密钥的条件之一)给服务端时使用的是公钥加密的,服务端收到后,会用私钥解密得到随机数。所以一旦服务端的私钥泄漏了,过去被第三方截获的所有 TLS 通讯密文都会被破解。
为了解决这个问题,后面就出现了 ECDHE 密钥协商算法,我们现在大多数网站使用的正是 ECDHE 密钥协商算法
3.3.3 ECDHE 握手过程
由于RSA 是比较传统的密钥交换算法,它不具备前向安全的性质,因此现在很少服务器使用的。而 ECDHE 算法具有前向安全,所以被广泛使用。
ECDHE 算法基于ECC 椭圆曲线特性,ECDHE算法的过程如下:
握手过程基本与RSA一致,只是传输的参数和生成共享密钥的算法不同
本文介绍的比较简单,感兴趣的可以自己去查一下资料,这一块涉及到挺多数学知识的
3.3.4 ECDHE 算法的优点
ECDHE算法提供前向安全性的原因在于它使用了临时生成的私钥(也称为临时密钥或会话密钥),并且每次通信都会重新生成这些私钥。这种设计确保了即使某个私钥被泄露,也不会影响其他通信会话的安全性,因为每次会话使用的密钥都是独立的。以下是详细解释:
临时私钥的生成:在ECDHE密钥交换过程中,通信双方都会生成一对临时的公私钥。这些私钥仅在当前会话中使用,并且在会话结束后就会被销毁。
密钥交换过程:双方使用对方的公钥和自己的私钥来计算一个共享的密钥。由于私钥是临时生成的,即使攻击者截获了公钥和共享密钥,他们也无法使用这些信息来计算出用于其他会话的私钥,因为每次会话的私钥都是不同的。
保护长期密钥:在ECDHE算法中,长期密钥(如果使用证书,则通常存储在证书中)仅用于签名和验证身份,而不直接参与密钥交换过程。这意味着即使长期密钥被泄露,攻击者也无法使用它来解密之前或之后的通信,因为每次通信都使用不同的临时密钥。
防止重放攻击:由于每次会话都使用新的随机数和临时密钥,即使攻击者截获了整个会话,他们也无法将这些信息重放来解密其他会话,因为每次会话的密钥都是唯一的。
密钥派生:最终的会话密钥是通过结合ECDHE算法计算出的共享密钥、客户端生成的随机数和服务端生成的随机数来派生的。这个过程确保了即使共享密钥被泄露,没有这些随机数也无法计算出最终的会话密钥。
3.3.5 总结
RSA 和 ECDHE 握手过程的区别:
RSA 密钥协商算法「不支持」前向保密,ECDHE 密钥协商算法「支持」前向保密;
使用了 RSA 密钥协商算法,TLS 完成四次握手后,才能进行应用数据传输,而对于 ECDHE 算法,客户端可以不用等服务端的最后一次 TLS 握手,就可以提前发出加密的 HTTP 数据,节省了一个消息的往返时间(这个是 RFC 文档规定的,具体原因文档没有说明,所以这点我也不太明白);
使用 ECDHE, 在 TLS 第 2 次握手中,会出现服务器端发出的「Server Key Exchange」消息,而 RSA 握手过程没有该消息;
3.4 HTTPS 的应用数据是如何保证完整性的?
TLS 在实现上分为握手协议和记录协议两层:
TLS 握手协议就是我们前面说的 TLS 四次握手的过程,负责协商加密算法和生成对称密钥,后续用此密钥来保护应用程序数据(即 HTTP 数据);
TLS 记录协议负责保护应用程序数据并验证其完整性和来源,所以对 HTTP 数据加密是使用记录协议;
TLS 记录协议主要负责消息(HTTP 数据)的压缩,加密及数据的认证,过程如下图:
具体过程如下:
首先,消息被分割成多个较短的片段,然后分别对每个片段进行压缩
使用摘要算法对压缩后的数据计算出一个摘要值,将计算出的摘要值通过特定的MAC算法生成消息认证码(MAC)。这个MAC值用于验证数据的完整性和来源
再接下来,经过压缩的片段再加上消息认证码会一起通过对称密码(在TLS握手阶段协商得到)进行加密
最后,上述经过加密的数据再加上由数据类型、版本号、压缩后的长度组成的报头就是最终的报文数据
记录协议完成后,最终的报文数据将传递到传输控制协议 (TCP) 层进行传输。
3.5 HTTPS传输就一定能够保证安全吗?
黑客在进行中间人攻击的时候,是可以伪造一个证书的,那么浏览器就会对拿到的证书进行校验,如果校验不通过,浏览器会有错误提示:类似“该网站证书非法,继续访问存在安全风险!”的提示字样。同时浏览器上面还会有一个点击按钮“继续访问”,如果点击了“继续访问”,进入了链接,那么用户的安全就不能得到保证了!再牛逼的技术也抵不住犯傻的用户啊!!!
3.6 使用 HTTPS 会被抓包吗?
一样的,也是要经过授权,即用户点击“继续访问”
本文参考
相关文章