文章目录
- HTTP
- 抓包工具
- [HTTP 协议格式](#HTTP 协议格式)
- [HTTP 请求 (Request)](#HTTP 请求 (Request))
-
- [URL 基本格式](#URL 基本格式)
- [URL encode](#URL encode)
- [认识 "方法" (method)](#认识 "方法" (method))
-
- [GET 方法](#GET 方法)
- [POST 方法](#POST 方法)
- [经典面试题: 谈谈 GET 和 POST 的区别](#经典面试题: 谈谈 GET 和 POST 的区别)
- 其他方法
- [认识请求 "报头" (header)](#认识请求 "报头" (header))
- [HTTP 响应详解](#HTTP 响应详解)
-
- [认识 "状态码" (status code)](#认识 "状态码" (status code))
- [认识响应 "报头" (header)](#认识响应 "报头" (header))
- [认识响应 "正文" (body)](#认识响应 "正文" (body))
- HTTPS
HTTP
HTTP (全称为 "超文本传输协议") 是⼀种应用非常广泛的应用层协议
HTTP是典型的"一问一答"模式的协议,请求和响应是一一对应的
- 多问一答:上传大文件的时候
- 一问多答:下载大文件的时候
- 多问多答:远程桌面
HTTP 往往是基于传输层的 TCP 协议实现的 (HTTP1.0, HTTP1.1, HTTP2.0 均为TCP,HTTP3 基于UDP 实现)。目前我们主要使用的还是 HTTP1.1 和 HTTP2.0 。本文讨论的HTTP 以 1.1 版本为主
我们平时打开一个网站, 就是通过 HTTP 协议来传输数据的
当我们在浏览器中输入 搜狗搜索 的 "网址" (URL) 时, 浏览器就给搜狗的服务器发送 HTTP请求, 解析请求后搜狗的服务器就会返回 HTTP 响应。这个响应结果被浏览器解析之后, 就展示成我们看到的页面内容(这个过程中浏览器可能会给服务器发送多个 HTTP 请求, 服务器会对应返回多个响应, 这些响应里就包含了HTML, CSS, JavaScript, 图片, 字体等信息)
所谓 "超文本" 的含义, 就是传输的内容不仅仅是文本(比如 html, css 就是文本), 还可以是一些其他的资源, 比如图片, 视频, 音频等二进制的数据
抓包工具
理解HTTP协议工作过程,以及理解HTTP协议报文格式,需要"抓包工具"
抓包工具相当于一个"代理"程序。但是代理程序不等于抓包工具,还有浏览器插件/加速器...也是代理程序
浏览器访问 sogou.com 时, 就会把 HTTP 请求先发给 Fiddler, Fiddler 再把请求转发给 sogou 的服务器。当 sogou 服务器返回数据时, Fiddler 拿到返回数据, 再把数据交给浏览器。因此 Fiddler 对于浏览器和 sogou 服务器之间交互的数据细节, 都是非常清楚的
如果设备上同时运行多个代理程序,可能会冲突,无法正常工作了。因此抓包的时候一定要确保设备上的其他代理程序处于关闭状态
浏览器内置了抓包工具
在浏览器按下 F12 打开开发者工具,选中网络(Network)这一个标签页, 每一条记录都是一次 HTTP 请求/响应
这个工具不太直观
Wireshark
不仅仅能抓到HTTP,也能抓到TCP, UDP, IP, 以太网数据帧...
功能太强了,使用门槛太高,抓HTTP缺失针对性的优化
Fiddler
专门用来抓HTTP, 能把网络上传输的HTTP数据获取到,并且显示出来
如果抓到的包非常少,可能需要手动开启抓取 https 的功能
左上角选择 Tools - Options...
选择 HTTPS 标签页, 里面的框全部勾选上,勾选的过程会弹出一个框,这是问你是否要安装证书,一定要选择"Yes"才能抓取HTTPS。如果点了"No"就得重装Fiddler才能继续
使用
左侧窗口显示了所有的 HTTP请求/响应, 可以选中某个查看详情
- 蓝色的里面是HTML数据
- 黑色的里面是普通数据
可以使用 ctrl + a 全选左侧的抓包结果, delete 键清除所有被选中的结果
- 右侧上方显示了 HTTP 请求的报文内容(切换到 Raw 标签页可以看到详细的数据格式)
- 右侧下方显示了 HTTP 响应的报文内容(切换到 Raw 标签页可以看到详细的数据格式)
- 请求和响应切换到Raw标签页时, 可以点击右下角的 View in Notepad 通过记事本打开, 在记事本中就能随意调整字体和大小了
HTTP是文本协议,但是返回的响应比较大的时候,就可能把响应的数据压缩成二进制数据后再返回。服务器最宝贵的硬件资源就是网络带宽。压缩,相当于是用cpu资源换取带宽资源。压缩后的数据到达客户端,再让客户端解压缩
对于浏览器,解压缩是自动完成的;对于Fiddler来说,就需要手动的解压缩
HTTP 协议格式
请求
把记事本的自动换行取消,这样更好地观察格式
一个HTTP请求报文,分为四个部分:
-
首行
包含三个部分(使用空格区分):
- 请求的方法 : GET, POST...
- 请求的网址(URL)
- 版本号
-
请求头(header)
包含若干行,每一行是一个键值对,键与值之间使用
:
分割 -
空行
header后面存在一个空行,类似于链表的null,作为结束标记
-
正文(body)
可选的,可以有或者没有。可以填写任意数据,自由定义格式和内容
响应
一个HTTP响应报文,分为四个部分:
-
首行
包含三个部分(使用空格区分):
- 版本号
- 状态码
- 状态码描述
-
响应的报头(header)
-
空行
响应头的结束标记
-
正文
HTTP 请求 (Request)
URL 基本格式
平时我们俗称的 "网址" 其实就是 URL (Uniform Resource Locator 统一资源定位符)
互联网上的每个文件都有一个唯一的URL,URL包含文件的位置以及浏览器应该怎么处理它。URL 的详细规则由 因特网标准RFC1738 进行了约定
- 协议方案名: 描述了URL接下来干啥, 常见的有 http 和 https, 也有其他的类型(例如访问 mysql 时用的jdbc:mysql )
- 登陆信息: 现在的网站进行身份认证一般不用这个了,所以都会省略
- 服务器地址: 要访问的服务器的域名/IP
- 端口号: 当端口号省略的时候, 浏览器会根据协议类型自动决定使用哪个端口。 例如 http 协议默认使用 80 端口, https 协议默认使用 443 端口。如果默认的不匹配,就要显式写出端口号
- 带层次的文件路径:描述了要访问服务器的哪个资源
- 查询字符串(query string): 确定了访问的资源后可能还要给一些补充信息。本质是一个个键值对。键值对之间使用 & 分隔; 键和值之间使用 = 分隔.
- 片段标识: 片段标识主要用于页面内跳转,区分页面不同的部分
URL 中的可省略部分
- 协议名: 可以省略, 省略后默认为 http://
- ip 地址 / 域名: 在 HTML 中可以省略(比如 img, link, script, a 标签的 src 或者 href 属性)。省略后表示服务器的 ip / 域名与当前 HTML 所属的 ip / 域名一致
- 端口号: 可以省略
- 带层次的文件路径: 可以省略。省略后相当于 / .
- 查询字符串: 可以省略
- 片段标识: 可以省略
URL encode
像 / ? : 这样的字符, 已经被url当做分隔符理解了, 因此这些字符不能随意出现。如果某个参数中需要带有这些特殊字符, 就必须先对其进行转义,不然可能跳转失败
中文字符由 UTF-8 或者 GBK 这样的编码方式构成, 虽然在 URL 中没有特殊含义, 但是仍然需要进行转义。 否则浏览器可能把 UTF-8/GBK 编码中的某个字节当做 URL 中的特殊符号。转义的规则如下: 将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位前面加上%,编码成%XY格式
例如在浏览器搜索C++
把URL复制粘贴到其他地方就显示出转码后的内容了
认识 "方法" (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 方法
GET 是最常用的 HTTP 方法, 常用于获取服务器上的某个资源
在浏览器地址栏中直接输入 URL或者点击收藏夹, 此时浏览器就会发送 GET 请求。另外, HTML 中的 link, img, script 等标签, 也会触发 GET 请求,还可以通过js构造
GET 请求的特点
- 首行的第一部分为 GET
- URL 的 query string 可以为空, 也可以不为空
- header 部分有若干个键值对结构
- body 部分习惯为空
关于 GET 请求的 URL 长度问题:
HTTP 协议由 RFC 2616 标准定义, 标准原文中明确说明没有对 URL 的长度有任何的限制。实际 URL 的长度取决于浏览器的实现和 HTTP 服务器端的实现。在浏览器端, 不同的浏览器最大长度是不同的, 但是现代浏览器支持的长度一般都很长; 在服务器端, 一般这个长度是可以配置的
POST 方法
POST 方法也是一种常见的方法, 多用于提交用户输入的数据给服务器(例如登陆页面)
通过 HTML 中的 form 标签可以构造 POST 请求, 或者使用 JavaScript 的 ajax 也可以构造 POST 请求
POST 请求的特点
-
首行的第一部分为 POST
-
URL 的 query string 一般为空 (也可以不为空)
-
header 部分有若干个键值对结构.
-
body 部分一般不为空。body 内的数据格式通过 header 中的 Content-Type 指定; body 的长度由 header 中的 Content-Length 指定
经典面试题: 谈谈 GET 和 POST 的区别
- 从本质上讲, GET 和 POST 没啥差别,只是使用习惯有差别
- 语义不同: GET 一般用于获取数据, POST 一般用于提交数据
- GET 的 body 一般为空, 需要传递的数据通过 query string 传递; POST 的 query string 一般为空, 需要传递的数据通过 body 传递
- GET 请求一般是幂等的, POST 请求一般是不幂等的(如果同样的请求多次得到的结果一样, 就视为请求是幂等的)
- GET 请求的结果可以被缓存,也可以被浏览器收藏夹收藏; POST 请求的结果不能被缓存(这一点也是承接幂等性)。设计一个浏览器可以缓存POST技术上完全可行,只是现在大部分浏览器都不支持
补充说明:
- 关于语义: GET 完全可以用于提交数据, POST 也完全可以用于获取数据
- 关于幂等性: 标准建议 GET 实现为幂等的, 实际开发中 GET 也不必完全遵守这个规则(主流网站都有"猜你喜欢" 功能, 会根据用户的历史行为实时更新现有的结果
- 关于安全性: 有些资料上说 "POST 比 GET 更安全", 这样的说法是不科学的。 是否安全取决于在传输密码等敏感信息时是否对其加密, 和 GET 还是 POST 无关
- 关于传输数据量: 有的资料上说 "GET 传输的数据量小, POST 传输数据量大", 这个也是不科学的。 标准没有规定 GET 的 URL 的长度, 也没有规定 POST 的 body 的长度。 传输数据量多少, 完全取决于不同浏览器和不同服务器之间的实现区别
- 关于传输数据类型: 有的资料上说 "GET 只能传输文本数据, POST 既可以传输文本,也可以传输二进制数据", 这个也是不科学的。 GET 的 query string 虽然无法直接传输二进制数据, 但是可以进行urlencode 转义后进行传输。虽然 POST 可以直接传输二进制数据,很多时候也是转义之后通过文本的方式传输
RESTful 风格:
请求方式 | 含义 |
---|---|
GET(SELECT) | 从服务器取出资源(一项或多项) |
POST(CREATE) | 在服务器新建一个资源 |
PUT(UPDATE) | 在服务器更新资源(更新完整资源) |
PATCH(UPDATE) | 在服务器更新资源, PATCH更新个别属性 |
DELETE(DELETE) | 从服务器删除资源 |
其他方法
- PUT 与 POST 相似,只是具有幂等特性,一般用于更新
- DELETE 删除服务器指定资源
- OPTIONS 返回服务器所支持的请求方法
- HEAD 类似于GET,只不过响应体不返回,只返回响应头
这些方法的 HTTP 请求可以使用 ajax 来构造,也可以通过一些第三方工具。还有任何一个能进行网络编程的语言都可以构造,本质上就是通过 TCP socket 写入一个符合HTTP 协议规则的字符串
认识请求 "报头" (header)
header 的整体的格式是 "键值对" 结构,每个键值对占一行,键和值之间使用分号分割
报头的种类有很多, 此处仅介绍几个常见的
-
Host
表示请求对应的服务器主机的IP地址和端口
-
Content-Length
表示 body 中的数据长度。这样知道了body有多长,就知道了一个完整的HTTP请求从哪到哪,解决粘包问题
-
Content-Type
表示请求的 body 中的数据格式
常见选项:
- application/x-www-form-urlencoded: form 表单提交的数据格式
- multipart/form-data: form 表单提交的数据格式(在 form 标签中加上enctyped="multipart/form-data" . 通常用于提交图片/文件
- application/json: 数据为 json 格式
-
User-Agent (简称 UA)
表示浏览器/操作系统的属性。可以根据UA区分出来当前设备是电脑还是手机,从而实现兼容,比如是电脑就返回一个宽屏的网页,手机就返回一个窄屏,并且按钮比较大的网页
-
Referer
表示这个页面是从哪个页面跳转过来的
如果直接在浏览器中输入URL, 或者直接通过收藏夹访问页面 Referer 是空的
-
Cookie
存储键值对格式的内容,和query string类似,都是程序猿自定义的,可以根据不同的需求定义不同的数据,增加HTTP的可扩展性,但往往有一个键值对用来标识用户的身份信息。Cookie这里的键值对,都是能够在客户端硬盘上持久化保存的
网页是运行在浏览器上的,一般情况下,网页不能直接访问客户端的硬盘,但有时候确实需要在客户端存储一些必要的信息,希望持久化存储。Cookie就是浏览器给网页提供的特定机制,不是让网页随意访问硬盘,而是对硬盘的操作做了特殊的封装,提供了一个/一组特殊的文件,只能在这个特殊的文件里写,并且是键值对的形式。这个文件是没法直接看到的,浏览器会分别给不同的网站(按照域名)创建一份Cookie文件,所以不同网站之间的 Cookie 并不冲突
服务器的响应返回Cookie数据给浏览器,存储下来,后续浏览器访问该网站的时候,就会在请求中把Cookie键值对发回给服务器
每个用户有不同的偏好, 服务器会给不同客户端响应个性化的Cookie数据,之后服务器就能从客户端请求的Cookie中了解到客户端的需求偏好了
会话id标识用户身份,以便查询到更多用户相关的信息
HTTP 响应详解
认识 "状态码" (status code)
状态码表示访问页面的结果 (是访问成功, 还是失败, 还是其他的一些情况...)
以下为常见的状态码:
-
200 OK
表示访问成功
-
404 Not Found
服务器没有找到客户端请求的资源
浏览器输入一个 URL, 目的就是为了访问对方服务器上的一个资源。 如果这个 URL 标识的资源不存在,那么就会出现 404
-
403 Forbidden
表示访问被拒绝。 有的页面需要用户具有一定的权限才能访问(登陆后才能访问)。 如果用户没有登陆直接访问, 就容易见到 403
-
405 Method Not Allowed
HTTP 中所支持的方法, 有 GET, POST, PUT, DELETE 等,但是服务器不一定都支持(或者不允许用户使用一些其他的方法)
-
500 Internal Server Error
服务器出现内部错误。 一般是服务器的代码执行过程中遇到了一些特殊情况(服务器异常崩溃)会产生这个状态码
-
504 Gateway Timeout
当服务器负载比较大的时候, 服务器处理请求的耗时就会很长, 就可能会出现超时的情况。这种情况在双十一等 "秒杀" 场景中容易出现, 平时不太容易见到
-
302 Move temporarily(临时重定向)
"重定向"就相当于手机号码中的 "呼叫转移" 功能,比如我原来的手机号是 186-1234-5678, 后来换了个新号码 135-1234-5678, 那么不需要让我的朋友知道新号码,只要我办理了"呼叫转移"业务, 其他人拨打 186-1234-5678 , 就会自动转移到 135-1234-5678 上
在POST请求中会经常见到 302 响应, 用于登陆成功后自动跳转到主页;还有的情况是访问的旧的网址,自动跳转到新的网址。响应报文的 header 部分会包含一个 Location 字段, 表示要跳转到哪个页面
-
301 Moved Permanently(永久重定向)
旧地址和新地址之间的映射关系是固定的,浏览器会缓存这样的映射关系,后续再次访问旧地址的时候,浏览器就可以直接构造新地址的请求,减少一次HTTP访问了。如果使用 302 作为重定向,旧地址是否要重定向,以及重定向到哪里是可变的,因此每次访问旧地址都需要访问服务器,获取到响应的Location 字段再进行跳转。301 也是通过 Location 字段来得知要重定向到的新地址
认识响应 "报头" (header)
响应报头的基本格式和请求报头的格式基本一致
类似于 Content-Type , Content-Length 等属性的含义也和请求中的含义一致
响应中的 Content-Type 常见取值有以下几种:
- text/html : body 数据格式是 HTML
- text/css : body 数据格式是 CSS
- application/javascript : body 数据格式是 JavaScript
- application/json : body 数据格式是 JSON
- 还可以指定字符集
认识响应 "正文" (body)
正文的具体格式取决于 Content-Type
HTTPS
HTTPS 是什么
HTTP 协议内容都是按照文本的方式明文传输的。 这就导致在传输过程中可能出现一些被篡改的情况。在互联网上, 明文传输是比较危险的事情!!!
HTTPS 也是应用层协议, 是在 HTTP 协议的基础上引入了一个加密层
HTTPS = HTTP + SSL(安全相关的协议)
"加密" 是什么
- 明文:要传输的真正数据
- 密文:加密之后的数据
- 密钥:用来加密和解密的数据
把明文 通过密钥 变成 密文 =>加密
把密文 通过密钥 变成 明文 =>解密
HTTPS 的工作过程
既然要保证数据安全, 网络传输中不能直接传输明文了, 而是加密之后的 "密文"
加密的方式有很多, 但是整体可以分成两大类: 对称加密 和 非对称加密
- 对称加密:加密和解密使用同一个密钥, 加密解密速度比较快
- 非对称加密:密钥是一对(分别称为 公钥 和 私钥),加密解密速度比较慢,安全性更高。使用公钥加密,此时就是私钥解密;使用私钥加密,此时就是公钥解密
所谓的安全都不是绝对的,只是破解的时间比较长,可能需要几十年
对称加密
对称加密其实就是通过同⼀个 "密钥" , 把明文加密成密文, 并且也能把密文解密成明文,就需要客户端和服务器都具有同一个对称密钥
但事情没这么简单, 服务器同一时刻其实是给很多客户端提供服务的。这么多客户端,用的密钥都必须是不同的(如果是相同的话, 密钥就太容易扩散了, 黑客也能拿到了)。 因此服务器就需要维护每个客户端和每个密钥之间的关联关系
比较理想的做法, 就是能在客户端和服务器建立连接的时候, 双方协商确定这次的密钥是啥
但是如果直接把密钥明文传输, 那么黑客也就能获得密钥了, 这样后续的加密操作就形同虚设了。因此密钥的传输也必须加密传输! 但是要想对密钥进行对称加密, 就仍然需要对密钥的密钥进行加密,无限循环下去。 此时密钥的传输再用对称加密就行不通了
就需要引入非对称加密的方式针对对称密钥进行加密。非对称加密的速度比对称加密慢很多,所以不直接使用非对称的方式加密业务数据
非对称加密
非对称加密要用到两个密钥, 一个叫做 "公钥", 一个叫做 "私钥",公钥和私钥是配对的
-
服务器生成 公钥 和 私钥。当客户端连上服务器的时候,服务器就把自己的公钥告诉客户端,私钥还是服务器自己持有
-
客户端在本地生成对称密钥(每个客户端生成自己的,与其他客户端不同), 通过公钥对对称密钥加密, 发送给服务器。 由于中间的网络设备(可能是黑客)没有私钥, 即使截获了数据, 也无法还原出对称密钥
-
服务器通过私钥解密, 还原出客户端发送的对称密钥并且使用这个对称密钥加密返回给客户端的响应数据。后续客户端和服务器的通信都只用这个对称密钥加密即可。由于对称加密的效率比非对称加密高很多, 因此只是在开始阶段使用非对称加密对称密钥, 后续的传输仍然使用对称加密
那么接下来问题又来了:
客户端如何确定这个公钥是不是黑客伪造的?
中间人攻击
黑客可以冒充自己是服务器, 获取到对称密钥
引入证书
服务端在使用HTTPS前,需要向CA机构(公证机构)申领一份数字证书。数字证书含有发证机构、证书有效期、证书所有者、服务器的公钥 、数字签名 等信息。服务器把证书传输给浏览器,浏览器从证书里获取公钥就行了,证书证明服务端公钥的权威性,客户端就能分辨出是不是服务器的公钥了
当服务端申请CA证书的时候,CA机构会对该服务端进行审核,并专门为该网站形成数字签名,过程如下:
- CA机构拥有非对称加密的私钥 pri 和公钥 pub
- CA机构对申请的证书的明文数据进行hash(缩小签名密文的长度,加快数字签名的验证速度),形成数据摘要
- 然后对数据摘要用私钥 pri 加密,得到数字签名。服务端申请的 证书明文 和 数字签名 共同组成了数字证书,这样一份数字证书就可以颁发给服务端了。通过证书解决中间人攻击, 在客户端和服务器刚建立连接的时候, 服务器给客户端返回一个数字证书,这个证书包含了服务器的公钥, 也包含了网站的身份信息。当客户端获取到这个证书之后, 会对证书进行校验(防止证书是伪造的)
- 判定证书的有效期是否过期
- 判定证书的发布机构是否受信任(操作系统中已内置受信任的证书发布机构)
- 验证证书是否被篡改: 从系统中拿到该证书发布机构的公钥, 对数字签名解密, 得到一个 hash 值, 设为 hash1。 然后自己再计算证书明文数据的 hash 值, 设为 hash2。 对比 hash1 和 hash2 是否相等。 如果相等, 则说明证书是没有被篡改过的,里面的公钥就是服务器的
黑客拿到数据摘要也很容易,毕竟系统中就有对应的公钥,但是无法篡改,也无法伪造
- 由于黑客不知道CA机构的私钥,所以hash之后无法用私钥加密形成数字签名,也就是不能改数字签名,一旦改了客户端就无法解密了,因为系统里没有对应的公钥
- 如果黑客只修改证书中的公钥(服务器公钥),不修改数字签名。客户端校验的时候就会发现自己算出来的数据摘要和数字签名中解密出来的数据摘要不一致,此时就会判定证书非法
中间人把证书整个掉包?
- 因为中间人没有CA私钥,所以无法制作假的证书
- 中间人只能向CA申请真证书,然后用自己申请的证书进行掉包
- 这个确实能做到证书的整体掉包,但是别忘记,证书明文中包含了域名等服务端认证信息,如果整体掉包,客户端依旧能够识别出来
Fiddler开启HTTPS的时候会有一个"安装证书"的过程,这个过程就是给你系统里新增一个Fiddler的公证机构。Fiddler抓包其实就是在进行中间人攻击,只不过这个过程是咱们用户可信任的,但是黑客没法做到这一点。系统中有哪些公证机构是可以查看的,被很多人盯着呢,如果制造商夹带私货,一旦被曝光出来,这个制造商以后就混不下去了
完整流程:
HTTPS 工作过程中涉及到的密钥有三组,其实一切都是围绕这个对称加密的密钥,其他的机制都是辅助这个密钥工作的
- 第一组(对称密钥): 客户端和服务器后续传输的数据都通过这个对称密钥加密解密
- 第二组(非对称密钥): 是为了让客户端把对称密钥加密传给服务器
- 第三组(非对称密钥): 用于校验证书是否被篡改,保证第二组非对称密钥是合法的