HTTP和HTTPS协议
- HTTP
- HTTP协议的工作过程
- HTTP协议格式
- 抓包工具
- 抓包结果
- HTTP请求(Request)
- URL
- 方法
- GET方法
- POST请求
- 其他方法
- 报头(header)
- Host
- Content-length
- Content-Type
- User-Agent
- Referer
- Cookie
- 正文(body)
- HTTP响应
- HTTP状态码
- 响应报头(header)
- 响应正文(body)
- 通过form表单构造HTTP请求
- form发送GET请求
- form发送POST请求
- 通过ajax构造HTTP请求
- 发送请求
- 通过第三方工具构造HTTP请求
- HTTPS
- 加密
- HTTPS的工作过程
- 对称加密
- 非对称加密
- 证书
- 数据摘要和签名
- 总结
HTTP
HTTP (全称为 “超文本传输协议”) 是一种应用非常广泛的 应用层协议.
HTTP 往往是基于传输层的 TCP 协议实现的. (HTTP1.0, HTTP1.1, HTTP2.0 均为TCP, HTTP3 基于 UDP
实现)
当我们在浏览器中输入一个 搜狗搜索的 “网址” (URL) 时, 浏览器就给搜狗的服务器发送了一个 HTTP 请
求, 搜狗的服务器返回了一个 HTTP 响应.
这个响应结果被浏览器解析之后, 就展示成我们看到的页面内容. (这个过程中浏览器可能会给服务器发送
多个 HTTP 请求, 服务器会对应返回多个响应, 这些响应里就包含了页面 HTML, CSS, JavaScript, 图片, 字
体等信息).
所谓 “超文本” 的含义, 就是传输的内容不仅仅是文本(比如 html, css 这个就是文本), 还可以是一些
其他的资源, 比如图片, 视频, 音频等二进制的数据
HTTP协议的工作过程
当我们在浏览器中输入一个 “网址”, 此时浏览器就会给对应的服务器发送一个 HTTP 请求. 对方服务器收
到这个请求之后, 经过计算处理, 就会返回一个 HTTP 响应
事实上, 当我们访问一个网站的时候, 可能涉及不止一次的 HTTP 请求/响应 的交互过程.
HTTP协议格式
抓包工具
抓包工具Fiddler 相当于一个 “代理”.
浏览器访问 sogou.com 时, 就会把 HTTP 请求先发给 Fiddler, Fiddler 再把请求转发给 sogou 的服务器.
当 sogou 服务器返回数据时, Fiddler 拿到返回数据, 再把数据交给浏览器.
因此 Fiddler 对于浏览器和 sogou 服务器之间交互的数据细节, 都是非常清楚的.
抓包结果
我此处抓到了我访问bilibili主页时的HTTP请求
分析HTTP报文结构
首行:方法+url+HTTP的版本
Header:请求的属性,冒号分隔的键值对,每组属性之间使用\n分隔,遇到空行表示Header部分结束
Body:空行后面的内容都是body,我此处抓的包body部分为空,body字符串允许为空,如果body存在,则在Header中会有一个属性Content-Length来表示body的长度
分析HTTP响应
首行:版本号+状态码+状态码解释
Header:请求的属性,冒号分隔的键值对,每组属性之间使用\n分割,遇到空行表示Header部分结束
Body:空行后面的内容都是Body,Body允许为空字符串则在Header中会有一个Content-Length属性来标识Body的长度; 如果服务器返回了一个html页面, 那么html页面内容就是在body中,此处访问bilibili主页就返回了bilibili的主页html
为什么HTTP报文中要存在空行
因为HTTP协议中并没有规定报头部分的键值对有多少个,空行就相当于是"报头的结束标记"或者是"报头和正文之间的分隔符"
HTTP协议在传输层依赖TCP协议,TCP协议是面向字节流的,如果没有这个空行,就会出现粘包问题
HTTP请求(Request)
URL
平时我们俗称的 “网址” 其实就是说的 URL (Uniform Resource Locator 统一资源定位符).
互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它.
http:协议方案名,常见的有http和https,也有其他的类型(例如访问mysql时用的jdbc)
user:pass : 登陆信息. 现在的网站进行身份认证一般不再通过 URL 进行了. 一般都会省略
v.bitedu.vip : 服务器地址. 此处是一个 “域名”, 域名会通过 DNS 系统解析成一个具体的 IP 地址. (通过 ping 命令可以看到, v.bitedu.vip 的真实 IP 地址为 118.24.113.28 )
端口号: 上面的 URL 中端口号被省略了. 当端口号省略的时候, 浏览器会根据协议类型自动决定使用哪个端口. 例如 http 协议默认使用 80 端口, https 协议默认使用 443 端口.
带层次的文件路径.
查询字符串(query string). 本质是一个键值对结构. 键值对之间使用& 分隔. 键和值之间使用 = 分隔.
片段标识:片段标识主要用于页面内跳转.
关于 query string
query string 中的内容是键值对结构. 其中的 key 和 value 的取值和个数, 完全都是程序猿自己约
定的. 我们可以通过这样的方式来自定制传输我们需要的信息给服务器.
URL 中的可省略部分
协议名: 可以省略, 省略后默认为 http://
ip 地址 / 域名: 在 HTML 中可以省略(比如 img, link, script, a 标签的 src 或者 href 属性). 省略后表示服务器的 ip / 域名与当前 HTML 所属的 ip / 域名一致.
端口号: 可以省略. 省略后如果是 http 协议, 端口号自动设为 80; 如果是 https 协议, 端口号自动设为 443.
带层次的文件路径: 可以省略. 省略后相当于 / . 有些服务器会在发现 / 路径的时候自动访问/index.html
查询字符串: 可以省略
片段标识: 可以省略
关于 URL encode
像 / ? : 等这样的字符, 已经被url当做特殊意义理解了. 因此这些字符不能随意出现.
比如, 某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义.
一个中文字符由 UTF-8 或者 GBK 这样的编码方式构成, 虽然在 URL 中没有特殊含义, 但是仍然需 要进行转义. 否则浏览器可能把
UTF-8/GBK 编码中的某个字节当做 URL 中的特殊符号.
转义的规则如下: 将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式
方法
GET方法
GET方法是最常用的HTTP方法,常用于获取服务器上的某个资源 在浏览器中直接输入url,此时浏览器就会发出一个GET请求,另外HTML中的link,img,script等标签,也会触发GET请求
GET请求的特点
首行的第一部分为GET
URL的query string可以为空,也可以不为空
header部分有若干个键值对结构
body部分为空
POST请求
POST方法也是一种常见的方法,多用于提交用户输入的数据给服务器(例如登陆页面)
通过HTML中的form标签可构造POST请求,或者使用JavaSciipt的ajax也可以构造POST请求
POST请求的特点
首行的第一部分为POST
URL的query string一般为空(也可以不为空)
header部分有若干个键值对结构
body部分一般不为空,body内的数据格式通过header中的Content-Typt指定,body的长度由header中的Content-Length指定
GET和POST的区别
语义不同:GET一般用于获取数据,POST一般用于提交数据
GET的body一般为空,需要传递的数据通过query string传递,POST的query string一般为空,需要传递的数据通过body
GET 请求一般是幂等的, POST 请求一般是不幂等的. (如果多次请求得到的结果一样, 就视为请
求是幂等的).
GET 可以被缓存, POST 不能被缓存. (这一点也是承接幂等性).
其他方法
PUT 与 POST 相似,只是具有幂等特性,一般用于更新
DELETE 删除服务器指定资源
OPTIONS 返回服务器所支持的请求方法
HEAD 类似于GET,只不过响应体不返回,只返回响应头
TRACE 回显服务器端收到的请求,测试的时候会用到这个
CONNECT 预留,暂无使用
报头(header)
header 的整体的格式也是 “键值对” 结构.
每个键值对占一行. 键和值之间使用分号分割.
Host
表示服务器主机的地址和端口
Content-length
表示body中的数据长度
Content-Type
表示请求的body中的数据格式
常见选项:
- application/x-www-form-urlencoded:form表单提交的数据格式,此时body的格式形如
title=test&content=hello
-
multipart/form-data:form 表单提交的数据格式(在 form 标签中加上enctyped=“multipart/form-data” . 通常用于提交图片/文件.)
-
application/json:数据为json格式 格式形如
{“username”:“123456789”,“password”:“xxxx”,“code”:“jw7l”,“uuid”:“d110a05ccde64b16a861fa2bddfdcd15”}
User-Agent
表示浏览器/操作系统的属性,形如
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36
其中 Windows NT 10.0; Win64; x64 表示操作系统信息
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.77 Safari/537.36 表示浏览器信息.
早期浏览器不断更新,支持的功能越来越丰富,新浏览器的功能多,旧浏览器的功能少,同一时刻,有的人用的是旧浏览器,有的人用的是新浏览器,服务器可以根据这个字段来判断是否要再这个浏览器带有某些功能
Referer
表示这个页面是从哪个页面跳转过来的,形如
https://v.bitedu.vip/login
如果直接在浏览器中输入URL, 或者直接通过收藏夹访问页面时是没有 Referer 的.
Cookie
Cookie 中存储了一个字符串, 这个数据可能是客户端(网页)自行通过 JS 写入的, 也可能来自于服务器(服务器在 HTTP 响应的 header 中通过 Set-Cookie 字段给浏览器返回数据).
往往可以通过这个字段实现 “身份标识” 的功能.
每个不同的域名下都可以有不同的 Cookie, 不同网站之间的 Cookie 并不冲突.
正文(body)
正文中的内容格式和header中的Content-Type密切相关,上面也罗列了三种常见的情况
HTTP响应
HTTP状态码
常见的HTTP状态码
200 OK
这是一个常见的状态码,表示访问成功
404 Not Found
没有找到资源
浏览器输入一个URL,目的就是为了访问对方服务器上的一个资源,如果这个URL标识的资源不存在,就会出现404
如果在浏览器中输入 www.sogou.com/index.html , 此时就在尝试访问 sogou 上的/index.html 这个资源.
如果输入正确就可以访问到,如果输入错误就会看到404这样的响应
403 Forbidden
标识访问被拒绝,有的页面通常需要用户具有一定的权限才能访问,如果没有权限直接访问,就容易见到403
405 Method Not Allowed
HTTP中的方法,例如GET,POST,PUT等方法,但是对方的服务器不一定都支持所有的方法,或者不允许用户使用一些其他的方法
500Internal Server Error
服务器内部出现错误,一般是服务器的代码执行过程中遇到了一些特殊情况(服务器异常崩溃)会产生这个状态码
504 Gateway Timeout
当服务器负载比较大的时候,服务器处理单挑请求的时候消耗的时间就会很长,就可能会导致出现超时的情况
302 Move temporarily
HTTP状态码302是一个重定向状态码,代表暂时性转移(Temporarily Moved)。当一个网页使用HTTP状态码302作为响应时,它返回的不是任何HTML内容,而是包含了一个附加的HTTP报头信息:Location,它指示另一个URL地址,在那里可以找到响应的内容。
HTTP状态码302和301都代表重定向,但它们有一些区别。301代表永久性转移(Permanently Moved),而302代表暂时性转移(Temporarily Moved)。此外,除非被报头信息Cache-Control或Expires明确指定,否则客户不会缓存结果。因为302也用于一些付费广告的页面,主要用于跳转。
总之,HTTP状态码302是在满足某个HTTP请求时必须被发出另一个请求的重定向状态码,它指示暂时性转移至另一个URL。
301 Move Permanently
永久重定向 当浏览器受到这种响应后,后续的请求都会被自动改成新的地址
301 也是通过Location字段来表示要重定向到新的地址
响应报头(header)
响应报头的基本格式和请求报头的格式基本一致
类似于 Content-Type , Content-Length 等属性的含义也和请求中的含义一致.
content-type
响应中的 Content-Type 常见取值有以下几种:
text/html : body 数据格式是 HTML
text/css : body 数据格式是 CSS
application/javascript : body 数据格式是 JavaScript
application/json : body数据格式是 JSON
响应正文(body)
正文的具体格式取决于content-type
通过form表单构造HTTP请求
form (表单) 是 HTML 中的一个常用标签. 可以用于给服务器发送 GET 或者 POST 请求.
form发送GET请求
<form action="https://www.sogou.com/" method="GET">
<input type="text" name="" id="">
<input type="text" name="" id="">
<input type="submit" name="" id="" value="提交">
</form>
在输入框输入数据,提交后就会构造HTTP请求并发送出去
from的action属性对应HTTP请求的URL
form的method属性对应HTTP请求的方法
input的name属性对应query string的key
input的内容对应query string的value
form发送POST请求
<form action="https://www.sogou.com/" method="POST">
<input type="text" name="userId" id="">
<input type="text" name="userName" id="">
<input type="submit" name="" id="" value="提交">
</form>
和GET方法的区别
method从GET变成了POST
数据从query string移动到了body中
通过ajax构造HTTP请求
从前端角度, 除了浏览器地址栏能构造 GET 请求, form 表单能构造 GET 和 POST 之外, 还可以通过 ajax 的方式来构造 HTTP 请求. 并且功能更强大
ajax 全称 Asynchronous Javascript And XML, 是 2005 年提出的一种 JavaScript 给服务器发送
HTTP 请求的方式.
特点是可以不需要 刷新页面/页面跳转 就能进行数据传输.
在 JavaScript 中可以通过 ajax 的方式构造 HTTP 请求.
首先从jquery cdn上获取ajax资源
发送请求
在 j s 中 , 是一个合法的变量名 , 在js中,是一个合法的变量名, 在js中,是一个合法的变量名,这个变量在jQuery中已经定义好的, 这个对象中就有很多方法 , 通过 这个对象中就有很多方法,通过 这个对象中就有很多方法,通过.的当时来调用对应的方法,$.ajax就是jQuery中封装好的,用来发起ajax请求的方法
通过这段代码,就可以向url所指向的地址发送一个请求,请求的内容是可自定义的
通过抓包工具可以获得我们发送的HTTP数据包,发现和我们指定的内容是一一对应的
通过第三方工具构造HTTP请求
通过postman工具也可以构造HTTP请求
HTTPS
HTTPS 也是一个应用层协议. 是在 HTTP 协议的基础上引入了一个加密层.
HTTP 协议内容都是按照文本的方式明文传输的. 这就导致在传输过程中出现一些被篡改的情况.
由于我们通过网络传输的任何的数据包都会经过运营商的网络设备(路由器, 交换机等), 那么运营商的网络设备就可以解析出你传输的数据内容, 并进行篡改.
加密
加密就是把明文(要进行传输的数据)进行一系列变换,生成密文,解密就是把密文在经历一系列变换还原成明文
在这个加密和解密的过程中, 往往需要一个或者多个中间的数据, 辅助进行这个过程, 这样的数据称为 密钥
HTTPS的工作过程
既然要保证数据安全, 就需要进行 “加密”.
网络传输中不再直接传输明文了, 而是加密之后的 “密文”.
加密的方式有很多, 但是整体可以分成两大类: 对称加密 和 非对称加密
对称加密
对称加密其实就是通过同一个 “密钥” , 把明文加密成密文, 并且也能把密文解密成明文.
一个简单的对称加密, 按位异或
假设 明文 a = 1234, 密钥 key = 8888
则加密 a ^ key 得到的密文 b 为 9834.
然后针对密文 9834 再次进行运算 b ^ key, 得到的就是原来的明文 1234.
(对于字符串的对称加密也是同理, 每一个字符都可以表示成一个数字)
当然, 按位异或只是最简单的对称加密. HTTPS 中并不是使用按位异或
引入对称加密之后, 即使数据被截获, 由于黑客不知道密钥是啥, 因此就无法进行解密, 也就不知道请求的真实内容是啥了.
但是事实上,一个服务器在同一时刻是需要给很多客户端提供服务的,这么多的客户端,每个人需要的密钥都必须是不同的,因此服务器就需要维护每个客户端和每个密钥之间的关联关系
理想的做法是,在客户端和服务器建立连接的时候,双方协商确定这次的密钥是什么
但是又出现了一个问腿,如果我们直接把密钥明文传输,就得到了和最开始一样的问题,当我们的密钥被黑客截获之后,我们就算加密传输,但是因为黑客手上保留有密钥,我们就相当于明文传输了
我们可以想到给密钥在加密?
这样就会进入到一种无限循环的过程中,此时就需要引入非对称加密
非对称加密
非对称加密要用到两个密钥, 一个叫做 “公钥”, 一个叫做 “私钥”.
公钥和私钥是配对的. 最大的缺点就是运算速度非常慢,比对称加密要慢很多.
- 通过公钥对明文加密, 变成密文
- 通过私钥对密文解密, 变成明文
也可以反着用 - 通过私钥对明文加密, 变成密文
- 通过公钥对密文解密, 变成明文
非对称加密的数学原理比较复杂, 涉及到一些 数论 相关的知识. 这里举一个简单的生活上的例子.
A 要给 B 一些重要的文件, 但是 B 可能不在. 于是 A 和 B 提前做出约定:
B 说: 我桌子上有个盒子, 然后我给你一把锁, 你把文件放盒子里用锁锁上, 然后我回头拿着钥匙来开锁取文件.
在这个场景中, 这把锁就相当于公钥, 钥匙就是私钥. 公钥给谁都行(不怕泄露), 但是私钥只有 B 自己持有. 持有私钥的人才能解密
客户端在本地生成对称密钥,通过公钥加密,发送给服务器
犹豫中间的网络设备没有私钥,即使截获了数据,也无法还原出内部的原文,也就无法获取到对称密钥
服务器持有私钥,通过私钥还原出客户端发送的对称密钥,并且使用这个对称密钥加密给客户端返回的响应数据
后续客户端和服务器的通信都只用对称密钥加密即可,由于这个密钥只有客户端和服务器两个人知道,所以其他设备截获数据也没有意义
但是问题在于,客户端如何获取公钥,且这个公钥不是黑客伪造的
证书
在客户端和服务器刚一建立连接的时候, 服务器给客户端返回一个 证书. 这个证书包含了刚才的公钥, 也包含了网站的身份信息.
这个整数可以理解成是一个结构化的字符串,包含了
- 证书发布机构
- 证书有效期
- 公钥
- 证书所有者
- 签名
…
当客户端获取到这个证书之后, 会对证书进行校验(防止证书是伪造的).
判定证书的有效期是否过期
判定证书的发布机构是否受信任(操作系统中已内置的受信任的证书发布机构).
验证证书是否被篡改: 从系统中拿到该证书发布机构的公钥, 对签名解密, 得到一个 hash 值(称为数据摘要), 设为 hash1. 然后计算整个证书的 hash 值, 设为 hash2. 对比 hash1 和 hash2 是否相等. 如果相等, 则说明证书是没有被篡改过的
数据摘要和签名
签名在一定程度上区分不同的数据,常见的生成签名的算法有MD5和SHA系列
以MD5为例,它的特点是
- 定长:无论多长的字符串,计算出来的MD5值都是固定长度(16字节或者32字节)
- 分散:源字符串只要改变一点点,最终得到的MD5值都会差别很大
- 不可逆:通过原字符串生成MD5很容易,但是通过MD5还原成源字符串理论上来说是不可能的
所以我们认为,如果两个字符串的MD5值相同,就认为这两个字符串相同
如何判定证书是否被篡改呢?
假设我们传入字符串1,根据这个字符1计算出hash1
我们就可以将字符串1和hash1从服务器传输给客户端,
此时客户端通过字符串1再次计算hash1,因为MD5的特点是分散和不可逆,如果有黑客篡改了字符串1的值,此时客户端根据这个字符串计算出来的hash值不是传输过来的hash1,就可以判断数据是否被篡改
此时又出现一个问题,如果黑客篡改了字符串1,
所以被传输的hash值不能明文传输,也需要传输密文
这个hash值在服务器端通过另一个私钥加密(这个私钥是申请证书的时候,证书发布机构给服务器的,不是客户端和服务器传输对称密钥的私钥)
客户端通过操作系统里的已经存的了证书发布机构的公钥进行解密,还原出原始的hash值,再进行校验
总结
HTTPS 工作过程中涉及到的密钥有三组.
第一组(非对称加密): 用于校验证书是否被篡改. 服务器持有私钥(私钥在注册证书时获得), 客户端持有公钥(操作系统包含了可信任的 CA 认证机构有哪些, 同时持有对应的公钥). 服务器使用这个私钥对证书的签名进行加密. 客户端通过这个公钥解密获取到证书的签名, 从而校验证书内容是否是篡改过.
第二组(非对称加密): 用于协商生成对称加密的密钥. 服务器生成这组 私钥-公钥 对, 然后通过证书把公钥传递给客户端. 然后客户端用这个公钥给生成的对称加密的密钥加密, 传输给服务器, 服务器通过私钥解密获取到对称加密密钥.
第三组(对称加密): 客户端和服务器后续传输的数据都通过这个对称密钥加密解密.