一、HTTP 协议是什么

每当我们打开浏览器,输入网址,点击链接,浏览网页时,背后都在发生着一场看不见的"数据接力"。这些数据是如何从服务器准确无误地传送到我们电脑上的?中间又经历了怎样的过程?今天,我们就来深入探讨互联网数据传输的基石——HTTP和HTTPS协议。

HTTP(HyperText Transfer Protocol,超文本传输协议)是一种应用非常广泛的应用层协议。是当前 web 开发中最核心的协议,但凡使用网站,都会用到HTTP协议。

那么,协议是什么意思?为了使数据能够完好无损地从源头发送到目的地,参与网络通信的收发方必须遵循相同的规则,这套规则就是协议,最终体现为网络上传输的数据包的格式。
简单来说,协议就类似一种暗号,只有懂得相同的暗号才能将数据传输过去。
在这里插入图片描述
HTTP诞生于1991年,目前已成为最主流的一种网络协议。

简单来说,当我们在浏览器中输入一个网址(URL),浏览器就会给服务器发送一个HTTP请求,服务器接收到请求后计算响应并返回HTTP响应。这个响应被浏览器解析之后,就展示成我们看到的内容了。

HTTP/1.0、HTTP/1.1和HTTP/2.0的底层基于TCP协议实现,而HTTP/3.0则是基于UDP协议实现。

二、HTTP 协议的工作过程

当我们访问一个网站,此时浏览器会给对应的服务器发送一个HTTP请求,服务器收到请求后经过计算得到一个HTTP响应,然后返回给浏览器,浏览器接收到响应我们就会看到网站内容了。
在这里插入图片描述
其实,当我们访问网站时,涉及到的HTTP请求/响应的交互可能不止一次。

我们当前学习的HTTP协议的特点就是一问一答,即有几条请求就会返回几条响应。
当然还有不同的收发模式:

  1. 多问一答:如上传一个大文件
  2. 一问多答:如下载一个大文件
  3. 多问多答:如远程控制程序

三、HTTP 协议格式

HTTP是一个文本格式的协议,我们可以通过两种方法查看协议细节:

  1. 浏览器自带的开发者工具,如Chrome按下F12可以开启开发者工具,然后点击Network就可以查看具体的通信交互细节了
  2. 使用抓包工具,我们使用Fiddler(专门抓包HTTP协议)来抓包HTTP协议以查看具体细节

3.1 Fiddler 抓包工具

什么是抓包工具?

抓包工具是一个能够获取到网络通信的数据包并且解析详细的格式的软件,电脑上的所有网络通信数据包都会先发给抓包工具,然后由抓包工具转发给服务器。

简单来说,抓包工具相当于一个代理,负责将请求/响应这些通信内容转发给服务器/浏览器,分为:

  • 正向代理:代替浏览器转发请求给对应服务器
  • 反向代理:代替服务器转发响应给浏览器

但是,未经授权的抓包操作可能涉及到信息安全,因此务必要注意抓包工具的使用!

目前较知名的抓包工具是 wireshark,可以抓包多个协议的数据包(HTTP、TCP、UDP、IP、以太网数据帧…),但是,使用的门槛较高,因此我们暂不使用。
Fiddler 是一个专门抓包HTTP协议的抓包工具,且使用起来简单、方便。

Fiddler官方下载地址
在这里插入图片描述
在这个页面下滑,找到 Fiddler Classic,我们要下载的就是这个版本(因为它免费~)
在这里插入图片描述
然后点击 “here” 跳转页面,点击 “Try For Free”,在这里插入图片描述
跳转到这个页面,输入信息就可以下载了(下载速度较慢的话可能需要魔法~)
在这里插入图片描述
下载完成后,点击 tool -> option 选项卡,然后点击 HTTPS在这里插入图片描述
第一次下载可能需要你信任Fiddler的根证书,这里一定要点击 YES!!否则就得卸载重装了~

然后页面就是这样(目前我的浏览器正在请求的是CSDN,因为我正在写文章):
在这里插入图片描述
可以看到,左边栏显示抓包到的数据包有很多,其实电脑上随时都会有很多程序在偷偷的运行。
这里的颜色也有不同:

  1. 红色表示报错,可能是请求失败了
  2. 蓝色表示请求得到了一个网页
  3. 绿色表示请求得到了一个JavaScript
  4. 灰色表示响应的数据已被缓存

右边上栏的就是请求的原始数据(要点击 raw),下栏的是响应的原始数据(原来是压缩了的,我这里是解压之后的了,点击中间的黄色的带有decode的那个条条就可以解压了)。

那为什么响应要压缩传输呢?其实是为了节省网络带宽,因为响应的正文部分比较长,如果直接传送会很吃带宽,而带宽资源又贵的很~

3.2 请求格式结构

我们通过 Fiddler 抓包到请求的原始数据,点击 “View in Notepad” 可以在记事本中打开原始数据
在这里插入图片描述
在这里插入图片描述
这个就是请求的原始数据,接下来我们来认识一下它的格式结构。

请求结构包含四个部分:

  1. 首行:包含了请求方法、URL和版本号
  2. 请求头(header):是键值对结构
  3. 空行:请求头的结束标志
  4. 正文(body):有些请求有正文有些则没有
    在这里插入图片描述

3.3 响应格式结构

在 Fiddler 的右下栏就是响应的数据,点击中间的黄色长条可以解压,然后点击 “raw” 就可以显示响应的原始数据了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
接下来我们来看看响应的结构。

响应的结构包含:

  1. 首行:包含协议版本、响应状态码和状态码描述。其中的响应状态码表示请求的处理结果:1xx是信息性状态,表示临时响应,与客户端继续操作;2xx是成功状态,表示请求已处理成功;3xx是重定向状态,表示资源已移动,需客户端重定向;4xx是客户端错误状态,表示请求有误(如URL错误);5xx是服务器错误状态,表示服务器处理失败。而状态码描述就是为了方便理解响应(100对应 continue,200对应 OK,302对应 Found,404对应 Not Found,500对应 Internal Server Error)
  2. 响应头(header):也是键值对结构
  3. 空行:是响应头的结束标志
  4. 正文(body):返回浏览器的内容,若是网站则是HTML
    在这里插入图片描述

3.4 协议格式结构总结

在这里插入图片描述

四、HTTP 请求详解(Request)

4.1 认识 URL

URL(Uniform Resource Locator统一资源定位符)就是用来描述网络上唯一资源的位置。互联网上的每一个文件都有一个唯一的URL,它包含了文件的位置以及浏览器如何使用等信息。

  • 这是一条标准的URL:
    http://user:pass@www.example.jp:80/dir/index.htm?uid=1#ch1
    在这里插入图片描述
  • 一个具体的URL(我的CSDN主页):
    https://blog.csdn.net/REGARD712?spm=1011.2415.3001.5343
    在这里插入图片描述

接下来我们来分析:

  1. https是一个协议名称,本质是 http + s(保护层ssl)起到保密作用,这里我们使用https协议访问CSDN网站,若是访问数据库MySQL,则应写成“jdbc:mysql”
  2. user:pass是登录信息,可以看到在这里省略了,因为现在网站进行身份认证一般不再通过URL了。
  3. blog.csdn.net服务器地址,这里是一个域名,域名可以通过DNS域名解析系统来转换成一个具体的IP地址
  4. 端口号,这里省略了端口号,当省略端口号,浏览器就会自动根据协议类型决定使用哪个端口号(http协议默认使用80端口,https协议默认使用443端口)
  5. 带有层次结构的路径,每一层相当于一个目录,目录中还可以有子目录
  6. 查询字符串(query string),对要访问的资源进行补充说明,也是键值对结构,键值对之间使用 “&” 来分隔,键和值之间使用 “=” 来分隔,程序员可以自己定义这里的内容
  7. 片段标识符,对于含有不同文档的网站来说,使用不同的片段标识符可以跳转到不同的文档,这里是CSDN主页没有包含文档,因此此处省略了

4.2 认识 URL encode

当我们搜索的时候输入汉字,比如搜索马斯克,看到浏览器上方的URL中的查询字符串中显示着"埃隆·马斯克"中文汉字,
在这里插入图片描述
于是复制下来整个 URL,却发现粘贴得到的 URL 的查询字符串中,原本是中文汉字的地方变成了一堆看不懂的字符:
在这里插入图片描述
这是怎么一回事?

其实,由于程序员可以自行定义查询字符串,如果定义的符号和原本 URL 指定的带有特殊含义的符号(如. / ? # &等等)冲突了,那就会将该符号转义。转义的规则是:将需要转码的字符由二进制转成十六进制,然后从右到左依次取4位,每2位做一位,在前面加上 “%”,最终编码成 “%XY” 的形式。

在这里,中文字符是由 UTF-8 或者 GBK 编码构成的,虽然在 URL 中没有特殊含义,但是仍然需要进行转义,否则浏览器可能把 UTF-8/GBK 编码中的某个字节当做 URL 中的特殊符号。

4.3 认识 “方法” (method)

方法 说明 支持的HTTP协议版本
GET 获取资源 1.0、1.1
POST 传输实体主体 1.0、1.1
PUT 传输文件 1.0、1.1
HEAD 获得报文首部 1.0、1.1
DELETE 删除文件 1.0、1.1
OPTIONS 询问支持的方法 1.1
TRACE 追踪路径 1.1
CONNECT 要求用隧道协议连接代理 1.1
LINK 建立和资源之间的联系 1.0
UNLINE 断开连接 1.0

这里有很多的方法,使用最多的方法是 GET 和 POST,因此我们主要讲一下这两个方法。

4.3.1 GET 方法

GET 方法是HTTP协议中最常用的方法,常用于获取服务器上的某个资源。

向浏览器网址栏输入一个 URL,此时浏览器就会发送一个 GET 请求给对应的服务器。

并且,HTML的 link、img 和 script 等标签也可以触发 GET 请求;JavaScript 中的 ajax 也可以构造出 GET 请求。

接下来,我访问一下我的 CSDN 主页并使用 Fiddler 抓包 GET 请求:
在这里插入图片描述

GET 请求中的特点:

  1. 首行的第一个是 GET
  2. URL 的查询字符串可以为空也可以不为空,这里不为空
  3. 请求报头header部分有若干个键值对
  4. 正文body部分为空
    在这里插入图片描述
  • 关于 GET 请求的 URL 的长度问题:
  1. 网上有些资料上描述,GET 请求长度最多1024KB,这样的说法其实是不正确的,HTTP 协议由 RFC 2616 标准定义, 标准原⽂中明确说明: "Hypertext Transfer Protocol – HTTP/1.1,"does not specify any requirement for URL length. 也就是说,标准定义中并没有限制 URL 的长度。
  2. 实际 URL 的长度取决于浏览器的实现和HTTP服务器端的实现。在浏览器端,不同的浏览器最大长度是不同的,但现代浏览器支持的长度一般都很长;在服务器端,长度一般都可以配置的。

4.3.2 POST 方法

POST 方法多用于登录界面,将用户信息提交给服务器,可以通过HTML中的 form 标签构造请求,也可以使用 JavaScript 的 ajax 构造构造请求。

通过 Fiddler 抓包我登录力扣时候的 POST 请求:
在这里插入图片描述
POST 请求中的特点:

  1. 首行的第一个是 POST
  2. URL 的查询字符串一般为空(也可以不为空)
  3. 请求包头header部分有若干对键值对
  4. 正文body部分一般不为空,body 中的数据格式是通过 header 中的 Content-Type 来指定,body 的长度由 header 中的 Content-Length 来指定
    在这里插入图片描述

面试题:谈谈 GET 和 POST 的区别


  • 语义上,并没有本质的区别,在一些场景可以混着用。GET 也可以用于提交数据,POST 也可以用以获取数据。只是在使用是有点细微差别:GET 习惯将数据放在 查询字符串(query string)中传输,而正文(body)部分则为空;而 POST 则习惯将数据放在正文(body)中传输,而查询字符串(query string)部分是空的。GET 请求一般是幂等的,而 POST 请求一般没有要求幂等。由于 GET 被要求是幂等的,就可以将数据缓存下来,而 POST 一般没要求幂等,一般也不允许将数据缓存。
  • 补充:幂等是什么意思?
    简单来说就是“执行一次和执行多次的结果是一致的”,比如双十一、抢票等这类抢购的场景,如果一个用户抢到了,并且进入支付页面,但是此时刚好卡了,用户就又点了两三次支付按钮,等到页面恢复正常,支付的金额并不会因为用户多点了两三次而多扣费,而是和用户点一次支付按钮的结果是一样的。
  • 关于 POST 比 GET 更安全的误解:
  1. 误解:网上有些人说,在登录的场景下,GET 比 POST 更加安全。因为若是 GET 请求,登录的时候用户输入的数据就会被放到 query string 中,而 query string 中的键值对是可以通过浏览器的 URL 栏看到的,但若是 POST 请求,数据就会被放到 body 中,在 URL 栏就无法看到了。
  2. 正解:安全与否取决于是否有加密以及加密的强度,与放在请求中的位置无关。因为 GET 和 POST 请求都可以通过抓包工具获取到,重要的是加密了没有。
  • 关于 GET 只能传输文本数据、POST 可以传输二进制的误解
  1. 误解:网上有人说 GET 只能传输文本数据;POST 既可以传输文本数据,也可以传输二进制数据
  2. 正解: GET 也可以传输二进制数据,虽然不能直接在 query string 中传输二进制数据,但是可以针对二进制数据通过 URL encode 转码,然后就可以放到 URL 中;GET 还可以直接将二进制数据放到 body 中(少部分情况)。

4.3.3 其他方法

PUT 方法

PUT 方法一般用于更新,相当于 SQL 中的 UPDATE 操作。

DELETE 方法

DELETE 方法一般用于删除服务器资源的,类似 SQL 中的 DELETE 操作。

HEAD 方法

HEAD 方法类似于 GET 方法,只不过返回的只有报头(header)部分,而不含正文(body)部分。

OPTIONS 方法

返回的是服务器所支持的请求方法。

TRACE 方法

显示服务器端的收到的请求,测试的时候用到

CONNECT 方法

预留,暂无使用。

这些方法的 HTTP 请求可以通过 ajax 来构造,也可以通过一些第三方工具来构造。

4.4 认识请求 “报头”(header)

报头(header)的整体格式是键值对结构,其中的键和值都是有标准规定的(RFC 标准文档),每一个键值对占一行,键值对之间用 “ ; ”分隔。

由于报头中的种类较多,这里只选择几个常见的:

  1. Host
  2. Content-Length
  3. Content-Type
  4. User-Agent
  5. Referer
  6. Cookie

4.4.1 Host

表示服务器主机的地址和端口号。里面的地址和端口号是当前请求所要访问的服务器的地址和端口号。

比如此时我正在写文章,使用 Fiddler 抓到当前页面的 GET 请求中,header 的 Host :
在这里插入图片描述
绝大部分情况下,Host 是和 “IP地址 :端口号” 这个格式一致的,即使是使用了代理,也可以通过 Host 获取到原本的目标。

4.4.2 Content-Length

Content-Length 指定 body 中的数据长度,单位是 “ 字节 ”。

由于 HTTP 请求是经过一定的格式将数据写入 TCP socket 中,然后发送给服务器的,TCP 在一次连接过程中可以进行多次通信(即发送多个请求),若请求中不包含 body ,服务器只需要读到空行就认为这个请求结束了;若请求中包含 body,就没办法区分该次请求到底什么时候结束,这时候返回去解析 header 中的 Content-Length ,然后根据这个值按字节数读取 body 内容。

4.4.3 Content-Type

Content-Type 指定 body 中的数据格式,提示服务器该如何解析(按照不同的数据格式使用对应的解析方法)body 中的数据。这是因为 HTTP 可以携带多种类型的数据(如 HTML、CSS、JavaScript、JSON 和图片),指定数据格式能帮助服务器准确区分并解析这些数据。

这些类型的数据的 Content-Type 如下:

  1. HTML:text/html
  2. CSS:text/css
  3. JavaScript:application/javascript
  4. JSON:application/json
  5. 图片:image/png 或 image/jpg

在这里插入图片描述

4.4.4 User-Agent(简称 UA)

user-agent 显示了用户所使用的设备的情况(客户端应用程序、操作系统、设备型号及浏览器等信息)。

在早期,由于不同浏览器和设备对网页功能的支持程度差异较大,服务器会通过 User-Agent 判断客户端能力,对旧设备或旧浏览器返回简化版页面,对新设备返回功能丰富的页面。

目前,用户的设备都大差不差,因此 UA 主要用于区分用户的设备是 PC端(Windows 或 MAC)还是移动端(Android 或 IOS),然后根据不同的设备返回不同版本的页面。

除了区分 PC端和移动端,UA 还可以:

  1. 统计分析,了解用户使用的设备、操作系统和浏览器的分布情况;
  2. 特定优化,针对某些浏览器(如 Chrome、Safari)的特殊功能或已知问题进行调整;
  3. 安全与反爬,辅助识别异常请求。

需要注意的是,User-Agent 可以被伪造或修改,因此不能完全依赖它做关键判断。

4.4.5 Referer

referer 记录了当前请求的来源页面的地址。

对于从某个网页点击超链接后跳转到另一页面或者提交表单,或者页面加载外部资源(如图片、样式表、脚本)这些操作,在跳转后页面的 HTTP 的请求报头中的 Referer 就会记录下跳转前页面的 URL;但是如果是直接在 URL 栏输入网址的 URL 或者通过书签打开,或者在浏览器中刷新页面这些操作,就不会显示 referer。

4.4.6 Cookie

cookie 是浏览器允许网页在客户端本地存储数据的一种机制,提供键值对存储机制。

cookie 是可以由后端程序员自行定义的,当客户端访问页面,发送了请求,服务器可以在响应中通过 Set-Cookie 头将 Cookie 发送给浏览器,浏览器收到后保存。在浏览器中可以直接查看当前页面的 cookie 内容。浏览器保存了当前页面的 cookie 后,就可以在后续给服务器发送请求的时候把这些 cookie 键值对放到请求 cookie header 中。

cookie 的一个业务场景是“登陆和用户认证”,当用户登录是发送了请求,其中包含了一些用户名密码等信息,当服务器收到请求,就会解析并查询数据库,服务器验证登录成功后,会生成一个唯一的 sessionId,并在服务器端创建对应的 session 对象(存储用户信息),然后将 sessionId 通过 Set-Cookie 返回给浏览器。浏览器后续请求会自动携带该 Cookie,服务器通过 sessionId 找到对应的 session 对象,从而识别用户身份。

Cookie 可以通过 Max-Age 或 Expires 属性设置有效期,未设置时默认为会话 Cookie(浏览器关闭即失效)。后端程序员可根据业务需求调整过期时间。
在这里插入图片描述

五、HTTP 响应详解(Response)

5.1 认识 “状态码”(status code)

状态码是由 RFC标准文档定义的、用于表示此次请求的结果是正常还是出错的三位数字代码。
在这里插入图片描述
由于状态码较多,这里只选取几个较常见的状态码。

5.1.1 200 OK

表示此次请求已成功,出现的大多数是这个响应码。

5.1.2 301 Moved Permanently

表示被请求的资源已永久移动到新位置,并且将来任何对此资源的引用都应该使用本响应返回的若干个 URL 之一。如果可能,拥有链接编辑功能的客户端应当自动把请求的地址修改为从服务器反馈回来的地址。

新的永久性的URL 应当在响应的 Location 域中返回。

如果这不是一个 GET 或者 HEAD 请求,浏览器就会禁止自动进行重定向,除非得到用户的确认,因为请求的条件可能因此发生变化。

注意:对于某些使用 HTTP/1.0 协议的浏览器,当它们发送的 POST 请求得到了一个301响应的话,接下来的重定向请求将会变成 GET 方式。

5.1.3 302 Move temporarily

请求的资源临时从不同的 URL 响应请求。由于这样的重定向是临时的,客户端应当继续向原有地址发送以后的请求。

简单来说就是当我们请求了一个资源,但是这个资源暂时移动到新位置,那么就会重定向到新位置(跳转)。

5.1.4 403 Forbidden

表示服务器已经理解请求,但是拒绝执行它。可能是客户端的请求的某个地方出现错误(网址输入错误等)。

5.1.5 404 Not Found

表示请求失败,请求所希望得到的资源未被在服务器上发现。出现这个错误的最有可能的原因是服务器端没有这个页面。

5.1.6 405 Method Not Allowed

表示请求行中指定的请求方法不能被用于请求相应的资源。

5.1.7 500 Internal Server Error

表示服务器遇到了一个未曾预料的状况,导致了它无法完成对请求的处理。一般来说,这个问题都会在服务器端的源代码出现错误时出现。也就是服务器的代码中出现了 bug。

5.1.8 504 Gateway Timeout

表示作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。可以理解为超时了~

5.1.9 状态码小结

状态码 类别 原因
1xx Informational(信息性状态码) 接收的请求正在处理
2xx Success(成功状态码) 请求正常处理完毕
3xx Redirection(重定向状态码) 需要进行附加操作以完成请求
4xx Client Error(客户端错误码) 服务器无法处理请求
5xx Server Error(服务器错误码) 服务器处理请求出错

由于响应报头(header)和正文(body)部分都与请求部分一致,因此不再过多赘述。

六、构造 HTTP 请求

6.1 通过 form 表单构造 HTTP 请求

构造 GET 请求

我们这里写一个 HTML 程序:

<form action="http://localhost:8080/hello" method="GET">
    <input type="text" name="userId" placeholder="请输入用户ID">
    <input type="text" name="content" placeholder="请输入内容">
    <input type="submit" value="提交">
</form>

保存后点开该页面:
在这里插入图片描述
输入相应信息之后点击 “提交” 按钮。

由于我们写的 URL是一个不存在的 URL,因此不会出现有效页面,也不会有响应返回。
在这里插入图片描述
使用 Fiddler 抓包得到请求:
在这里插入图片描述

构造 POST 请求

将代码中的 GET 改为 POST,再一次保存后通过抓包得到请求:
在这里插入图片描述
可以发现原本在 URL 部分的 query string 现在在 body 中了,其他部分没有什么改变。

七、HTTPS 协议是什么

HTTPS 协议也是一个应用层协议,是在 HTTP 协议的基础上引入了一个加密层。

由于 HTTP 协议是通过文本格式明文传输的,因此在传输过程中很容易被劫持并篡改

很常见的情况是 “运营商劫持”:

当我们想下载某个应用软件,但是应用商城中没有,这时候去浏览器上查找并下载(非官方网址),当下载完成后却发现下载的是另一个应用,这就是发生了篡改。

当我们点击下载按钮的时候,浏览器会向应用服务器发送一个 HTTP 请求,服务器返回的响应中包含了应用软件的下载链接,这时候劫持者会将原来的下载链接篡改成另一个软件的下载链接,具体如下:

运营商节点说明

获取应用a的下载链接

获取应用a的下载链接

返回应用a的下载链接

将应用a的下载链接
替换为应用b的下载链接

浏览器

运营商

应用a的服务器

运营商负责转发请求与响应
可能在此环节进行劫持

因此,未经过加密的明文传输的安全性十分低,也是很危险的事情。

HTTPS 协议就是将 HTTP 协议的内容进行加密,进一步保证用户的信息安全。

八、HTTPS 协议的工作过程

8.1 加密

加密是将明文(未被保护的数据内容)经过一系列变换转换为密文;而 解密 则是一个相反的过程,将密文经过一系列变换还原为明文。

在加密和解密的过程中,通常需要多个中间数据来辅助进行,这样的数据称为 密钥

HTTPS 采用密文传输,首先需要对数据进行加密。

加密的方式有很多,但是总体分为两大类:对称加密非对称加密

8.1.1 对称加密

对称加密,就是通过同一个 “密钥”,对数据进行加密和解密。

一个最简单的对称加密就是 “按位异或”:

比如明文 a = 1234,密钥 key = 8888

  • 加密:通过 a ^ key 加密后得到的密文 b = 9834
  • 解密:通过 b ^ key 解密后得到的明文 a = 1234

当然,HTTPS 协议不使用按位异或这么简单的加密方式来加密。

下面是一个引入了对称加密的通信过程:

服务端

客户端

密文请求

密文请求

将明文请求通过密钥
加密为密文请求

黑客入侵路由器
截获请求内容

将密文请求通过密钥
解密为明文请求

引入对称加密后,即使黑客截获了数据,由于不知道密钥,也就无法知道数据内容。

但是服务器是需要能够在同一时间给多个客户端提供服务的,如果针对每一个客户端的密钥都相同,那就很容易将密钥泄露,因此每一个客户端的密钥都必须不同。但是这样一来,服务器就需要维护所有客户端及其对应的密钥的关联关系,十分麻烦。

比较理想的情况是:在客户端和服务端建立链接的时候,双方就协商确定密钥

其大概的流程是:

  1. 在客户端和服务端建立链接的时候,就协商确定本次通信的会话密钥
  2. 但是在必须得将密钥通过明文发送给对方,否则对方不知道密钥是什么
  3. 后续的数据内容都通过密钥加密再发送

但是,如果在传输密钥的时候被黑客截获了,那密钥的作用也就失去了意义。

如果想要对密钥进行加密传输,又需要先明文发送一个加密密钥的密钥……

这时候对称加密就无法满足需求,于是引入了非对称加密。

8.1.2 非对称加密

非对称加密使用两个不同的密钥,一个是 “公钥”,一个是 “私钥”。这两个密钥是配套 ,最大缺点是运行速度非常慢,比对称加密慢很多。

通过非对称加密来加密解密的过程是:

  1. 加密:通过公钥/私钥对明文加密,生成密文
  2. 解密:通过私钥/公钥对明文解密,还原明文

公钥给谁都可以,但是想要解密(公钥解密)必须使用私钥,如果只持有公钥,就无法解密。

大致的通信流程如下:

  1. 客户端和服务端在建立链接的时候,服务端会明文传输公钥返回给客户端,自己保留私钥。这时候若被黑客劫持获取到公钥也无需担心
  2. 客户端收到公钥后,在本地生成一个对称密钥,通过公钥加密后传输给服务端(只能使用私钥解密)
  3. 若传输过程中被黑客截获,黑客手里只有公钥而没有私钥,无法解密
  4. 服务端获取到密文后通过私钥解密获取到客户端生成的对称密钥,后续的通信内容都通过对称密钥加密传输

由于对称加密的效率比非对称加密高很多,因此非对称加密只在协商密钥阶段才使用非对称加密,后续的传输仍然使用对称加密。

服务端

客户端

双方建立链接

发送公钥
即使被黑客截获也无需担心

发送加密后的
对称密钥

发送加密后的
对称密钥

请求建立链接

建立链接

生成对称密钥
通过公钥加密后发送

黑客入侵路由器
截获请求内容
由于没有私钥
无法解密

通过私钥解密得到对称密钥
后续通信内容
都使用对称密钥加密

但是,如果黑客也掌握了非对称加密的技术,自己有一套公钥和私钥,并且在客户端和服务端建立链接的时候将服务端发送给客户端的公钥偷梁换柱成自己的公钥,然后再转发给客户端,这时候客户端获取到的是黑客的公钥(无法分辨),以为是服务端发送的,于是使用黑客的公钥加密对称密钥后发送,这时候黑客截获了并使用自己的私钥解密就获取到对称密钥了。这样的情况称为中间人攻击

客户端需要验证收到的数据内容是否被篡改,于是引入数字证书机制。

数字证书

服务端在用 HTTPS 协议进行通信之前,需要向 CA 机构(Certificate Authority,是权威第三方机构)申领一份数字证书,可以理解为一个结构化的字符串,包含了以下信息:

  • 证书发布机构
  • 证书有效期
  • 公钥
  • 证书所有者
  • 签名
  • ……

服务器在和客户端建立链接的时候就将数字证书发送给客户端,客户端接收到证书后先验证其内容是否有效,然后再从证书中获取公钥。这样可以有效防止公钥被篡改的情况发生。

服务端在和客户端建立链接之前先申领证书,然后将证书发送给客户端,过程大致如下:

2.审核信息

审核结果

1.申请认证(证书)

3.签发证书

4.返回证书

6.密钥协商

客户端

验证过程

• 计算明文信息INFO的摘要
D_cal = Hash(INFO)

• 用CA证书的公钥解密签名
D_pem = Dec_by_pub_CA[Sig]

• 对比两个摘要
D_cal == D_pem

• 验证其它信息
域名/有效时间/是否吊销

5.验证证书

服务端

服务端准备过程

• 生成公钥与私钥对
pub_svr & pri_svr

• 确认申请信息
域名/申请者/公钥

• 生成请求文件.csr
不含有私钥信息

审核

CA 机构

证书

明文信息-INFO

• 签发机构 CA_qq

• 有效时间 xxxx/x/x - xxxx/x/x

• 扩展信息 ...

• 域名: cdn.qq.com

• 申请者: TEG/APD

• 公钥: pub_server

签名
Sig = Enc_by_private_CA[Hash(INFO)]

当服务端申请 CA 证书的时候,CA 机构会对该服务端进行审核,并生成一个属于该网站的数字签名,过程大致如下:

  1. CA 机构持有非对称加密的 私钥 A 和 公钥 A’
  2. CA 机构对服务端申请的证书明文数据进行 hash,形成数据摘要
  3. 然后通过 CA 的 私钥 A 对数据摘要进行加密,得到数字签名 S

服务端申请的证书明文数字签名 S 共同组成了一份数字证书,这样就可以直接颁发给服务端了。

有了数字证书防止公钥被篡改,也就可以有效避免 “中间人攻击” 情况的发生了。

  1. 当客户端和服务端建立连接的时候,服务端会给客户端发送数字证书(包含公钥和身份信息)
  2. 客户端接收到数字证书后验证该证书(是否过期、发布机构是否在操作系统可信任机构中等等)是否被篡改:从系统中拿到该证书发布机构的公钥,对签名解密得到一个 hash 值(称为数据摘要),设为 hash1;然后计算整个证书的 hash 值,设为 hash2。对比 hash1 和 hash2 是否相等,如果相等则说明证书未被篡改过。

中间人可以篡改数字证书吗?

  • 如果中间人篡改了证书的明文,但是没有 CA 机构的私钥,无法 hash 之后用私钥加密形成签名,也就没有办法对篡改后的证书形成匹配的签名
  • 如果强行篡改,客户端收到该证书后会发现 明文 和 签名解密后的值 不一致,就知道证书已经被篡改过了,从而终止想服务器传输信息,防止信息泄露给中间人

中间人能把数字证书掉包吗?

  • 中间人没有 CA 私钥,无法制作假的数字证书,因此只能向 CA 机构申请真证书,然后用该证书掉包
  • 即使被调包,客户端收到证书后一校验依旧能够发现证书明文中的身份信息、域名等是不对的

文章到这里就告一段落啦,若有错误请尽管指出~

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐