一.介绍HTTP
HTTP全称为超文本传输协议,是一种应用非常广泛的应用层协议。目前,主流使用的HTTP版本是HTTP1.1和HTTP2.0,在这边文章中,讨论的是HTTP1.1。
使用浏览器,打开手机上的APP或者是后端程序,都是分布式/微服务体系结构,也就是服务器之间的相互调用,这些大概率都是使用的HTTP。
HTTP最早一些版本是基于TCP,而在HTTP3.0是基于UDP。HTTP协议也是一种典型的一问一答模型的协议(一问一答模型:客户端给服务器发送一条请求,服务器返回一条响应,形成了一一对应,构成了一问一答模型)打开网页就是一问一答模型。
二.HTTP报文格式
通过抓包工具来观察到HTTP请求/响应的详细情况(抓包:获取到通过网卡上的数据,并且解析显示出来)本篇文章使用的抓包软件是fiddler。
1.HTTP请求格式
i.首行
以fiddler抓搜狗浏览器为例,GET是HTTP请求的方法,https://sogou.com/是URL访问的资源, HTTP/1.1是HTTP的版本号。
ii.请求头(head)
从第二行开始,一直到空行结束都是请求头:
请求头中的每一行都是一个键值对,键和值之前通过 :+空格 分隔开。
iii.空行
请求头的结束标记。
iv.正文(body)
有的请求中会有body,但是有的请求中就没有body,刚刚抓的搜狗浏览器的包中 的请求中就没有body。
通过某登入网页,抓了有body请求的包,图中画横线的地方就是body正文,抓的这个包的请求的方法不是GET了,而是POST,需要注意的是,GET一般都没有body,因为GET是从服务器中获取数据,而POST一般有body是因为,POST是把数据给服务器。
2.HTTP响应格式
i.首行
HTTP/1.1是版本号,200状态码,OK是状态码的描述。(状态码是用来表示成功或者失败的原因,并且首行之间不同的部分都会用空格分隔)
ii.响应头(head)
响应头中也是键值对,每一行是一个键值对,通过空行来进行结尾,键和值之间使用:+ 空格来分割。键值对也是标准规定的,有的键值对只能出现在请求中,有的只能出现在响应中,有的都可以出现。
iii.空行
响应报头的结束标记。
iv.正文(body)
对于响应来说,正文通常都是HTML/CSS/JS/JSON/图片/音频/字体......
3.URL
URL(Uniform Resource Locator,统一资源定位符)是用于标识和定位互联网上资源(如网页、图片、文件等)的字符串,俗称“网址”。它规定了资源的位置及访问方式,是互联网资源寻址的核心工具。(URI唯一资源标识符)
(1)URL基本格式
协议方案名:URL是给哪个协议使用
登录信息:当前网站已经不再使用这样的方式进行身份认证了
服务区地址:IP或是域名(确定唯一的主机)
服务器端口号:确定主机上的程序(不写端口号,浏览器会根据http(80端口)或https(443端口)协议自动拼接)
带层次的文件路径:确定服务器上的具体资源
查询字符串:键值对结构,针对访问的资源进行补充说明
片段标识符:表示一个页面中的某个部分,常见于一些文档类网站
(2)URL Encode
防止URL中因为一些特殊符号导致解析失败
转义规则:
把要转的符号/汉字每个字节按照十六机制的方式表示出来,给每个字节前面加上%就可以了。
三.HTTP方法
1.GET
(1)直接在浏览器中输入URL,就会触发GET请求
(2)HTML页面中很多元素会进一步触发GET请求
但凡是域名中带有sogou字样的都是这个页面进一步触发的请求。当前看到的这些请求是有搜狗主页的HTML进一步触发的,HTML包含一些link,script,a,img.......都可能进一步的触发HTTP get请求,进一步从服务器获取资源。
访问一个页面不是一次HTTP请求能够搞定的,而是通过多个HTTP(GET)请求最终汇总得到的结果。
上述是通过ctrl + F5 得到的请求内容,这些内容主要是一些CSS文件,JavaScript文件,图片文件,字体文件等(这些内容一般都是固定的,改变频率非常低)
ctrl + F5是忽略缓存,强制从服务器重新读取数据,而F5是为了节省服务器贷款,加快了页面展示的速度,并没有清理缓存,而是将第一次访问搜狗主页时,把上述内容保存下来也就是缓存。后续访问时,没必要重新获取上述内容。
(3)JS代码中也能够触发GET请求
2.POST
(1)登录/注册
登入gitte时,fiddler的抓包post信息:
(2)上传文件
在gitte中更换头像时,上传图片,fiddler的抓包post信息:
这里body中的内容,就是头像图片中的二进制内容(base64转码后的内容),图片本身是二进制的,HTTP协议虽然也能传输二进制,但是很多时候也会把二进制数据进行转码(不一定是会用url encode(转码体积太大))
(3)GET和POST区别
首先,GET和POST本质上没有什么区别,GET场景下能使用时,POST也能用,POST能使用的场景,GET也能使用。
但是GET和POST并不代表没有区别,其中存在一些区别。
i.语义
GET表示从服务器中拿数据,POST表示往服务器提交数据。如果就想使用GET提交数据,并不是不行,只是说不建议。就像使用POST来获取数据也是可以,但是也不建议。
ii.传递数据方式不同
GET传递数据,通常是通过查询字符串(query string)把自定义数据交给服务器,POST传递数据,通常通过body把自定义数据交给服务器。
在GET中也能加body(不建议这样做,而且有些库,不支持解析GET的body)给POST也能加query string一般也不会这样做,有的产品为了设计简单一致,一股脑全部使用POST
iii.幂等性
GET方法对应的请求,通常设计成幂等的,POST方法对应的请求,对于幂等性则无要求。
幂等性的概念:
幂等性(Idempotence)是指同一请求多次执行与一次执行的效果相同。具体来说,无论客户端发送多少次相同的请求,服务器端资源的状态最终保持一致。幂等性是HTTP方法设计的重要原则之一,尤其在网络不可靠(如超时、重试)或客户端行为不确定(如用户重复提交)的场景下,幂等性能够有效避免资源状态的意外改变。
(iv)继承幂等性
GET如果设计成幂等的,此时GET的结果是可以被缓存的(浏览器针对css,js,图片等缓存,运营商CDN,运营商的反向代理,服务器这里的一些缓存(代码集成/单独部署)),POST不设计成幂等的,POST就不应该被缓存。
3.其他方法
四.请求报头和响应报头
1.host
表示服务器主机的地址和端口这部分信息在URL中已经有所体现了。
2.Content-Length和Content-Type
Content-Length:表示body中的数据长度,单位是字节。
Content-Type:表示请求的body中的数据格式,其值也是固定的(text是文本)
Content-Type的属性会直接在这个属性中指定body的编码方式,浏览器会按照UTF8解析body的内容,如果这里写的编码方式和body实际的方法不匹配(或者根本不写),页面就会出现乱码情况。
浏览器会直接根据这里的值决定如何解析body的内容。Content-Length和Content-Type都属于在请求和响应中都会存在的。
一个请求/响应中,没有body,也就没有这两个字段,如果有body,则必须有这两个字段。因此Content-Length和Content-Type也告诉我们这一个HTTP数据报到这里就结束了。
如果一个HTTP数据报,没有body,此时空行就相当于分隔符了,如果一个HTTP数据报中有body,此时Content—Length就描述了body的长度。
3.User-Agent(简称UA)
上述的信息包含操作系统信息(版本)和浏览器信息(版本)
UA目前的作用是用于做数据统计的,可以统计很多的业务指标,根据统计结果,进一步的迭代改进产品。UA的统计主要就是用来区分PC和移动端。
在以前,UA字段获取到用户的浏览器信息和操作系统信息后,就可以判定当前用户的浏览器版本都支持哪些特性,比如说只支持文字图片,还是说能支持多媒体等等。
以前通过UA来解决浏览器页面的问题,手机和电脑的屏幕尺寸是不同的,需要对页面进行排版和功能展示,就需要通过UA的方式进行切入,平板这样特殊的设备也是能够通过UA来进行切入。
4.Refer
描述了当前页面是从哪个页面跳转过来的,Refer不一定有,有时候可能会被隐藏。
5.Cookie
Cookie也是键值对结构,上述的键值对看似是从浏览器通过请求发送给服务器的,实际上这些数据最初都是从服务器返回给浏览器的。此处看到的这些数据,都相当于是在浏览器本地存储。
Cookie是属于浏览器给网站提供的一种“客户端存储数据”的机制,因为为了安全,浏览器进制网页直接访问用户的硬盘,但是浏览器并没有把路堵死,并且开了一个口子。
允许网页通过键值对的方式来存储数据(这样的数据本质上也是在硬盘上的)具体这样的键值对是如何存储到硬盘上的,浏览器已经封装好了, 网页本身无法干预。
Cookie里面的内容也来自于服务器,首次访问某个网站时,可能是不带Cookie的,在响应中就会有Set-Cookie这样的Header,把一些键值对写回到浏览器这边,浏览器后续访问这个网站就会带有Cookie。
Cookie这样的键值对是按照域名为维度来分类的,一个域名下可能有多个Cookie,后续访问哪个域名就把这个域名下的Cookie带入到请求中。
总结Cookie
(1)Cookie的定义:Cookie是浏览器本地持久化存储数据的一种机制,按照键值对方式存储,键值对内容都是程序员自定义的,按照域名为维度分别进行存储。
(2)Cookie的来处:服务器返回的响应数据报中,包含Set—Cookie字段。
(3)Cookie的去处:后续浏览器访问同一个服务器的时间,就会把之间存储的Cookie再带上,从而发送到服务器这边。
验证用户是否登入,并且当前状态是否为登入状态:
五.状态码
HTTP状态码是服务器在响应客户端请求时返回的三位数字代码,用于表示请求的处理结果。它们分为五个类别,以首位数字区分。
1xx(信息响应)
表示请求已被接收,继续处理。属于临时响应,客户端应等待最终响应。
100 Continue
客户端应继续发送请求体。用于客户端发送较大请求前,确认服务器是否接受请求(通过Expect: 100-continue
头部)。101 Switching Protocols
服务器同意切换协议(如升级到WebSocket或HTTP/2)。102 Processing (WebDAV)
服务器已接收请求但尚未完成处理,避免客户端超时。
2xx(成功响应)
表示请求已成功被服务器接收、理解并处理。
200 OK
请求成功,响应中包含请求的数据(如GET或POST)。201 Created
资源已创建(如通过PUT或POST)。响应应包含Location
头部指向新资源。202 Accepted
请求已接收但未处理完成(适用于异步任务)。204 No Content
请求成功,但响应中无内容(常用于DELETE或更新操作)。206 Partial Content
服务器返回部分内容(响应范围请求,通过Range
头部实现)。
3xx(重定向)
需客户端进一步操作以完成请求。通常用于URL重定向或缓存控制。
301 Moved Permanently
资源已永久迁移到新URL。客户端应更新书签,后续请求直接访问新URL。302 Found
资源临时位于不同URL。客户端应本次请求新URL,但未来可能仍用原地址。303 See Other
响应结果位于另一个URL(强制客户端用GET访问新URL,避免重复提交POST)。304 Not Modified
资源未修改(客户端使用缓存副本,通过If-Modified-Since
或ETag
验证)。307 Temporary Redirect
与302类似,但要求客户端保持原HTTP方法(如POST)。308 Permanent Redirect
与301类似,但要求客户端保持原HTTP方法。
4xx(客户端错误)
客户端请求有误,服务器无法处理。
400 Bad Request
请求语法错误(如参数错误、格式无效)。401 Unauthorized
需要身份验证(如未提供凭证或凭证错误)。响应需包含WWW-Authenticate
头部。403 Forbidden
服务器拒绝请求(权限不足,如无访问资源的权限)。404 Not Found
请求的资源不存在,或服务器不愿透露存在性。405 Method Not Allowed
URL不支持请求的HTTP方法(响应应包含Allow
头部列出允许的方法)。406 Not Acceptable
服务器无法提供客户端要求的资源表示形式(如请求的Accept
头部不被支持)。408 Request Timeout
服务器等待请求超时,客户端可重试。409 Conflict
请求与服务器当前状态冲突(如文件版本冲突)。410 Gone
资源已永久删除,无新地址。413 Payload Too Large
请求体超过服务器限制。415 Unsupported Media Type
请求的媒体类型不被支持(如上传格式错误)。429 Too Many Requests
客户端发送过多请求(限流场景)。
5xx(服务器错误)
服务器处理请求时发生错误。
500 Internal Server Error
服务器内部错误(通用错误,无具体信息)。501 Not Implemented
服务器不支持请求的功能(如未实现的HTTP方法)。502 Bad Gateway
服务器作为网关或代理时,从上游服务器收到无效响应。503 Service Unavailable
服务器暂时不可用(如过载或维护)。可配合Retry-After
头部提示重试时间。504 Gateway Timeout
网关或代理服务器未能及时从上游获取响应。505 HTTP Version Not Supported
服务器不支持请求的HTTP协议版本。
其他特殊状态码
418 I'm a teapot (RFC 2324)
彩蛋状态码,表示服务器是“茶壶”,无法煮咖啡。451 Unavailable For Legal Reasons
因法律原因资源不可访问(如政府审查)。
六.HTTPS
1.运营商劫持
我需要下载天天动听的软件,一般下载的话就是直接是这个软件的安装包,但是如果被运营商劫持之后,这个安装包可能就是其他的软件了。
2.加密
(1)明文:要传输的原始数据
(2)密文:把明文进行加密后,得到一个让别人不能理解的数据
(3)加密:明文 -> 密文
(4)解密:密文 -> 明文
(5)密钥:进行加密和解密的主要数据/辅助工具
(6)公钥:公开出来的密钥
(7)私钥:私藏起来的密钥
i.对称加密
两类加密算法,加密还是解密都是使用同一个密钥
ii.非对称加密
一对密钥,A和B,用A加密,则要用B解密,或者用B加密,用A解密。
3.基本工作过程
HTTPS是在HTTP的基础上加入了密钥
HTTP明文的数据传输,容易被黑客劫持
1)引入对称加密
由于服务器对应多个客户端,不能让每一个客户端的密钥相同(因为密钥相同,劫持者可以自己创一个客户端获取密钥,从而进行解密)因此每个客户端,需要自己生成一个不同的密钥。
因此就需要让每个客户端连上服务器时,自己生成一个随机的对称密钥,服务器也得拿到这个密钥才能进行解密,就引入了传输对称密钥给服务器。
2)传输对称密钥给服务器
按照这样的传输模式,密钥传输给服务器时,此时黑客就可能截获到这个密钥,后续的加密形同虚设。
3)引入非对称加密
通过非对称加密将要传输的对称密钥进行加密,客户端通过公钥对密钥进行加密,即使公钥是公开的,也必须使用私钥才能对其进行解密,所以通过黑客的网络设备并没有影响。
引入非对称加密不是为了取代对称加密,而是为了辅助它进行加密,也因为非对称加密的运算量开销比较大,比较消耗性能。
这里的业务数据仍然使用的是对称加密传输,这里的非对称加密只是用来加密传输对称加密。
4)中间人攻击
服务器先生成一对公钥(pub1)和私钥(pri1),客户端向服务器询问公钥是什么时,会经过被黑客入侵后的路由器,黑客再把这个请求发给服务器。
此时服务器返回公钥会经过黑客入侵的路由器,黑客收到公钥是什么之后,自身在生成公钥(pub2)和私钥(pri2),把自己生成的公钥传给客户端。
客户端无法知道这个公钥是否是黑客伪造的,只能相信这个信息。客户端接受pub2后,将对称密钥基于pub2来进加密并且发送给服务器后,黑客会在路由器处劫持。
黑客劫持后,使用pri2进行解密获取对称密钥,再将对称密钥使用pub1进行加密发送给服务器。此时服务器也无法知道这个传回来的数据是否被黑客动了手脚。
这样黑客就能将客户端和服务器中传输的数据进行更改。
5)引入证书机制
为了让客户端识别出,拿到的公钥不是伪造的,需要通过第三方公正机构,当需要搭建服务器且使用HTTPS时,就需要在公正机构这里,申请证书(例如网站的域名,营业执照,备案号,公钥等等······)
公正机构会通过上述的信息生成一个电子证书,服务器获申到证书之后,后续客户端从服务器就不是只拿公钥这么简单,而是拿的整个证书。
此时客户端凭借证书中的数字签名(保证合法性的关键要点),对证书的合法性进行校验。签名是通过校验和+加密获取的(校验和:原始数据相同,计算得到的校验和就相同,校验和不同,说明原始数据就不同)
加密是通过证书中的各个字段综合在一起,计算校验和后进行非对称的加密(公正机构自己生成一对公钥和私钥,公正机构自己保存好自己的私钥,公钥则会发给各个客户端)公正机构拿着自己的私钥对刚才的校验和进行加密,就得到了数字签名。
客户端验证数字签名,先将整数中的各个字段再算一次校验和,得到checkSum1,再使用公正机构的公钥,对数字签名进行解密,得到checkSum2。
对比checkSum1和checkSum2,如果相等,视为当前证书的各个字段,就是和服务器这边发出来的证书是一模一样的,此时就可以认为这是合法证书。
如果不相等,意味着证书上的内容被中间黑客篡改过了,此时浏览器往往就会弹出警告页面,提示用户,该网站不安全。
黑客截取请求后,如果篡改数据,想要更新数字签名是不行的,数字签名需要使用公正机构的私钥进行加密,这个私钥黑客是无法拿到的。黑客如果自己生成一个私钥,客户端拿着公正机构的公钥无法对其数据进行解密。此时客户端解密出错也可以认为是证书有问题,也会弹出危险窗口。
4.Fiddler可以抓包的原因
使用抓包工具时,会需要你先信任抓包工具的证书,信任抓包工具的证书之后,此时你手里就有fiddler的公钥了,此时fiddler就相当于一个你认可的公正机构。
此时浏览器发送咱们的证书是啥,fiddler再将请求转发给服务器,此时服务器的证书传给fiddler,fiddler此时篡改证书中的数据,把证书中的公钥改成fiddler的公钥,在通过自身重新生成新的证书,重新计算校验和并且使用自己的私钥进行加密,得到数字签名。
此时fiddler将改过的证书发送给浏览器,浏览器通过fiddler的公钥进行解密就可以获取到fiddler篡改后的数据了。后续客户端使用fiddler的公钥加密对称密钥,fiddler通过私钥解密,再将对称密钥的数据使用服务器的公钥加密对称密钥后再传输给服务器。