引言
这是在我阅读了肖佳老师的书《HTTP抓包实战》后对HTTP相关内容的总结,主要还是报文相关(记录:阅读此书与写出此文章耗时5天)
HTTP协议报文的结构
HTTP报文分2个:一个是HTTP请求报文,一个是HTTP响应报文
HTTP请求报文(Request)
HTTP请求报文分为3个部分,第一部分叫起始行(Request line),第二部分叫首部(Request Header),第三部分叫主体(Body)
- 第一行中有Method(请求方法)、URI和协议版本。例如GET https://blog.yexca.xyz/ HTTP/2
- 第二部分是Header(首部)
- 第三部分是Body(主体)
注意:Header首部和Body主体之间有一个空行
HTTP响应报文(Responese)
HTTP响应报文与请求报文的结构基本一样,也分为3个部分,第一部分叫响应行(Response line),第二部分叫响应首部(Response Header),第三部分是0主体(Body)
- 第一行有协议版本、状态码和状态码消息。例如HTTP/2 200
- 第二部分是Header(首部)
- 第三部分是Body(主体)
注意:Header首部和Body主体之间有一个空行
HTTP协议请求方法和状态码
URL格式
URL的全称为Uniform Reasource Locator,中文译名为统一资源定位符,用于完整地描述Internet上某一处资源的地址
URL的基本格式如下:
schema://host[:port#]/path/.../[?query-string][#anchor]
属性 | 描述 |
---|---|
schema(协议) | 指定底层使用的协议(例如:http,https,ftp) |
host(域名) | HTTP服务器的IP地址或者域名 |
port#(端口) | HTTP服务器的默认端口是80,这种情况下端口号可以省略。如果使用了别的端口,则必须指明 |
path(资源的路径) | 访问资源的路径 |
query-string(参数) | 发送给HTTP服务器的数据 |
anchor(锚) | 锚,页面内部超链接 |
HTTP请求方法
号 | 方法 | 描述 |
---|---|---|
1 | GET | 请求指定的页面信息并返回实体主体 |
2 | HEAD | 类似于GET请求,只不过返回的响应中没有具体的内容,用于获取报头 |
3 | POST | 向指定资源提交数据进行处理请求(例如提交表单或者上传文件),数据被包含在请求体中。POST请求可能会导致新的资源的建立和/或对已有资源的修改 |
4 | PUT | 从客户端向服务器传送的数据取代指定文档的内容 |
5 | DELETE | 请求服务器删除指定的页面 |
GET与POST的区别
- GET提交的数据会放在URL之后,以
?
分隔URL和传输数据(即query-string,键值对方式),参数之间以&
相连 而POST方法是把提交的数据放在HTTP包的Body中 - GET提交的数据大小有限制(因为浏览器对URL的长度有限制) 而POST方法提交的数据大小没有限制
- GET方式需要使用Request.QueryString来取得变量的值 而POST方法通过Request.Form来获取变量的值
HTTP状态码
HTTP状态码存在于HTTP的响应报文中,其作用是Web服务器用来告诉客户端发生了什么事
HTTP状态码被分为5大类,随着协议的发展,HTTP规范中会定义更多的状态码吧
状态码 | 已定义范围 | 分类 |
---|---|---|
1XX | 100~101 | 信息提示,表示请求已被成功接收,继续处理 |
2XX | 200~206 | 成功,表示请求已被成功接收、理解、接受 |
3XX | 300~305 | 重定向,要完成请求,必须进行进一步的处理 |
4XX | 400~415 | 客户端错误,请求有语法错误或请求无法实现 |
5XX | 500~505 | 服务器错误,服务器未能实现合法的请求 |
常见状态码
名称 | 释义 |
---|---|
200 | OK:服务器成功处理了请求 |
301/302 | Moved Permanently(重定向):请求的URL已移走。Response中应该包含一个Location URL,说明资源现在所处的位置 |
304 | Not Modified(未修改):客户端的缓存资源是最新的,需要客户端使用缓存 |
404 | Not Found:未找到资源 |
401 | 禁止访问 |
501 | Internal Server Error:服务器遇到一个错误,使其无法对请求提供服务 |
206(Partial Content,部分内容)
206状态码代表服务器已经成功处理了部分GET请求(只有发送GET方法的HTTP请求,Web服务器才可能返回206)
应用比如说使用下载工具实现断点续传或者在线视频播放都是使用206状态码来实现
例如现在打开视频网站的一个视频,对于视频所在的URL
- 浏览器会发送一个GET请求,Header中包含Range: bytes=5303296-5336063,意思就是请求得到5303296-5336063之间的数据
- Web服务器返回一个206的HTTP响应。Header中包含Content-Range: bytes 5303296-5336063/12129376,表明这次返回的内容范围
301与302(Moved Permanently,重定向)
在得到301或302响应后,浏览器会再次请求位于Location中返回新的URL
状态码301和302在语法上是一模一样的,都是在HTTP响应的Location中返回新的URL
区别在于:
- 301表示旧地址已经被永久移除了,这个资源不可访问了,搜索引擎会把权重算到新地址 例如:防止用户输错域名或更换域名
- 302表示旧地址的资源还在,仍然可以访问,这个重定向只是临时地从旧地址跳转到新地址,搜索引擎会把权重算到新地址 例如:未登录状态下访问需要登陆才能访问的页面
304(Not Modified,未修改)
状态码304表示上次的文档已经被缓存了,还可以继续使用
400(Bad Request)
状态码400表示客户端请求有语法错误,发送的HTTP请求中的数据有错误,例如表单有错误或者Cookie有错误,不能被度武器所理解
401(Unauthorized)
状态码401是指为授权错误。有些网页采用的是HTTP基本认证(Basic Authentication),需要在HTTP请求Header中带上Authentication,否则服务器会返回状态码401
404(Not Found)
该状态码表明服务器上无法找到请求的资源。除此之外,也可以在服务器端拒绝请求但不想说明理由时使用
例如BV1AB4y1D7Ft这个视频仅在登录并且收藏的情况下才可见,否则将返回404
403(Forbidden)
状态码403表示Web客户端发送的请求被Web服务器拒绝了。如果服务器想说明为什么拒绝请求,可以在Body中描述原因。但这个状态码通常表示服务器不想说明拒绝原因
500(Internal Server Error)
状态码500代表服务器内部错误。出现错误的原因有很多,比如代码的错误、数据库连接语句出错、程序内部抛出异常、空指针错误等
503(Server Unavailable)
状态码503表示服务器暂时不可用。由于服务器维护或者过载,服务器目前无法处理请求
这个状况是临时的,并且将在一段时间以后恢复
了解全部状态码
HTTP协议Header
Header的语法格式是“key: value”,一行一个。每一个Header都有特殊的作用
Cache相关的Header
HTTP请求与HTTP响应都有很多用于缓存的Header。HTTP缓存是指当Web请求抵达缓存时,如果本地有“已缓存的”副本,就可以从本地存储设备而不是原始服务器中获取该文件
Cookie
Cookie是一种HTTP Cache,是HTTP中非常重要的内容。它由key=value的形式组成,比如ip_country=CN
浏览器把Cookie通过HTTP请求中的“Cookie: header”发送给Web服务器,Web服务器通过HTTP响应中的“Set-Cookie: header”把Cookie发送给浏览器
Accept
Accept表示浏览器客户端可以接受的媒体类型。例如Accept: text/html代表可以接受服务器返回html
通配符*
代表任意类型,例如Accept: text/html,\/*;q=0.8*代表浏览器可以处理所有的类型。一般浏览器客户端给Web服务器发送的都是类似这个
Accept-Encoding
Accept-Encoding跟压缩有关,浏览器发送HTTP请求告诉Web服务器浏览器支持的压缩形式,例如Accept-Encoding: gzip, deflate
Accept-Language
Accept-Language作用是声明自己接受的语言。注意语言与字符集的区别,中文是语言,中文有多种字符集,例如GB2312、GBK等。例如Accept-Language: en-US,en;q=0.8,zh-CN;q=0.6,zh;q=0.4,zh-TW;q=0.2
User-Agent
User-Agent的作用是浏览器用来告诉服务器,客户端使用的操作系统及版本、CPU类型、浏览器及版本、浏览器渲染引擎、浏览器语言、浏览器插件等
例如User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:103.0) Gecko/20100101 Firefox/103.0代表64位Linux系统,Firefox是103.0版本
如果想要模拟各种不同的客户端,只要修改User-Agent,就可以伪装成各种客户端
Referer
Referer主要用来让服务器判断来源页面,即用户是从哪个页面来的,网站通常用其统计用户来源,也可以用作放盗链等
Connection
从HTTP/1.1起,系统默认都开启了Connection: Keep-Alive,保持连接特性。Keep-Alive不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如Apache)中设定这个时间
Host
Host的作用是指定被请求的主机和端口号,如果是80端口号会被自动隐藏
HTTP协议中的缓存
缓存无处不在,有浏览器端的缓存、服务器端的缓存、代理服务器的缓存,还有ASP.NET页面的缓存、对象缓存、数据库缓存等等
HTTP中具有缓存功能的是浏览器缓存和代理服务器缓存
HTTP缓存是指当Web请求抵达缓存时,如果本地有“已缓存的”副本,就可以从本地存储设备而不是从原始服务器中提取这个文档
缓存的优点:减少了冗余的数据传输,节省了传输时间;减少了服务器的负担,大大提高了网站的性能;加快了客户端加载网页的速度等
如何判断缓存新鲜度
Web服务器通过以下两种方式来判断浏览器缓存是否最新
- 浏览器把缓存文件的最后修改时间通过Header的If-Modified-Since告诉Web服务器。浏览器收到HTTP请求后,在Header中将文件最后修改世界Last-Modified与请求报文的If-Modified-Since相比较。若相同则说明文件是最新的,则发送状态吗304(Not Modified)给浏览器客户端;若不同则发送状态码200把最新文件发送给浏览器客户端
- 浏览器把缓存文件的ETag通过Header的If-None-Match告诉Web服务器
与缓存有关的Header
- HTTP请求报文Header
名称 | 释义 |
---|---|
Cache-Control: max-age=0 | 以秒为单位 |
If-Modified-Since: Tue, 28 Jun 2022 00:50:56 GMT | 缓存文件的最后修改时间 |
If-None-Match: “1771e0c387823da5329c20a76bece83c” | 缓存文件的ETag值 |
Cache-Control: no-cache | 不使用缓存 |
Pragma: no-cache | 不使用缓存 |
- HTTP响应报文Header
名称 | 释义 |
---|---|
Cache-Control: public | 响应被缓存,并且可以被多用户访问使用 |
Cache-Control: private | 响应只能作为私有缓存,特定用户使用 |
Cache-Control: no-cache | 提醒浏览器要从服务器提取文档进行验证 |
Cache-Control: no-store | 绝对禁止缓存(用于机密、敏感文件) |
Cache-Control: max-age=60 | 60s后缓存过期(相对时间) |
Date: Thu, 01 Sep 2022 21:56:36 GMT | 当前响应发送的时间 |
Expires: Thu, 01 Sep 2022 21:57:37 GMT | 缓存过期的时间(绝对时间) |
Last-Modified: Tue, 28 Jun 2022 00:50:56 GMT | 服务端文件的最后修改时间 |
Etag: “1771e0c387823da5329c20a76bece83c” | 服务器文件的ETag值 |
注:浏览器总是优先使用cache-control,如果没有时才考虑Expires
ETag
ETag是Entity Tag(实体标签)的缩写,是根据实体内容生成的一段hash字符串(类似于MD5或者SHA1之后的结果),可以表示文件的状态。当资源发生改变时,ETag也随之发生改变
使用ETag主要是为了解决一些Last-Modified无法解决的问题,比如说某些服务器不能精确得到文件的最后修改时间、一些文件最后修改时间改变了但是内容不变、某些文件修改特别频繁甚至达到了以秒为单位以下等
注:Last-Modified只能精确到秒
浏览器不使用缓存
使用Ctrl+Shift+R快捷键强制刷新浏览器,可以让浏览器不使用缓存,即浏览器的HTTP请求报文的Header中带有Cache-Control: no-cache,明确告诉Web服务器不使用缓存
注:Pragma: no-cache与Cache-Control: no-cache作用相同,只是Pragma: no-cache是HTTP/1.0定义的,保留为了兼容性
直接使用缓存,不经过服务器验证
使用Ctrl+R快捷键刷新浏览器,浏览器会去Web服务器验证缓存
如果在地址栏直接输入地址并访问,浏览器会“直接使用有效的缓存”,不会发送HTTP请求去服务器验证缓存,这种情况叫作缓存命中
公有缓存与私有缓存
公有缓存Cache-Control: public可以由多个用户共享访问,而私有缓存Cache-Control: private只能单个用户访问使用
HTTP协议压缩和URL Encode
HTTP压缩是指Web服务器和浏览器之间压缩传输文本内容的方法。HTTP采用通用的压缩算法,比如用gzip来压缩HTML、JavaScript、CSS文件
HTTP内容编码与压缩的区别
在HTTP协议中,可以对Body部分进行编码,如可以采用gzip这样的编码,从而达到压缩的目地;也可以使用其他编码方式把内容搅乱或加密,以此来防止未被授权的第三方看到文档的内容。所以HTTP压缩其实就是HTTP内容编码的一种
HTTP压缩的过程
- 浏览器发送HTTP请求Header中带Accept-Encoding: gzip,deflate告诉服务器浏览器支持gzip压缩
- Web服务器接到HTTP请求后,先生成原始的HTTP响应,包含原始的Content-Type与Content-Length;然后通过gzip对HTTP响应的Body进行编码,并在编码后Header中的Content-Type与Content-Length替换为压缩后的大小,以及加上编码方式Content-Encoding: gzip;再把HTTP响应发送给浏览器
- 浏览器接到HTTP响应后,根据Content-Encoding: gzip来对HTTP响应进行解码,获取到原始HTTP响应后显示出网页
注:HTTP请求也是可以编码的,但是浏览器一般不会对HTTP请求编码
内容编码类型
HTTP定义了一些标准的内容编码类型,并允许用扩展的形式添加更多的编码
在Header中的Content-Encoding就是使用这些标准化的代号来说明编码时使用的算法
编码 | 描述 |
---|---|
gzip | 表明实体采用GNU zip编码 |
compress | 表明实体采用UNIX的文件压缩程序 |
deflate | 表明实体是用zlib的格式压缩的 |
identity | 表明没有对实体进行编码。当Header中没有Content-Encoding时,默认为此情况 |
gzip、compress以及deflate编码都是无损压缩算法,用于减少传输报文的大小,不会导致信息缺失。其中gzip通常效率最高,使用最为广泛
深入理解Cookie机制
HTTP协议是无状态的,对于浏览器的每一次请求,服务器都会独立处理,不与之前或之后的请求发生关联。即使是同一个浏览器发送了3个请求,服务器也会独立处理这3个请求,服务器并不知道这3个请求是来自同一个浏览器
会话机制与Cookie机制
服务器需要识别浏览器请求,就必须弄清楚浏览器的请求状态。既然HTTP协议是无状态的,那就让服务器和浏览器共同维护一个状态,这就是会话机制
- 浏览器第一次请求服务器时,服务器创建一个会话,并将会话ID作为响应的一部分发送给浏览器
- 浏览器存储会话ID,并在后续请求中带上会话ID
- 服务器取得请求中的会话ID就知道是不是同一个用户了
这样后续请求与第一次请求就产生了关联,而Cookie机制就是一种会话机制
服务器在内存中保存会话对象,浏览器可以使用Cookie机制保存会话ID
Cookie是什么
Cookie是浏览器用来存储少量数据的一种机制,数据以key=value形式存储,多个Cookie之间以分号;
分隔,浏览器发送HTTP请求时自动附带Cookie信息
Cookie最主要的作用是用来做用户认证,还可以用于保存用户的一些其他信息。也可以用于互联网精准广告定向技术,例如用户浏览了某些商品,就可以用Cookie记录下来,然后进行大数据深度分析,实现广告精准投放
鉴于此项,目前欧洲的一些国家已经对Cookie立法,并规定必须经过用户的允许才可以保存用户的Cookie
Cookie的属性
根据网站不同,Cookie有所不同
- Expires
表示Cookie失效的时间,如果不指定则在关闭浏览器或页面时被浏览器删除
- Path
表示Cookie所属的路径,asp.net默认为/
也就是根目录
假设在同一个服务器上的目录如下:/test/
、/test/cd/
、/test/dd
,Cookie1的Path在/test/
,Cookie2的在/test/cd/
,那么/test/
下的所有页面都可以访问到Cookie1,而/test/dd/
的自页面不能访问Cookie2.因为Cookie只能让其Path路径下的页面访问
- HttpOnly
这是个关乎安全方面的属性,将一个Cookie设置为HttpOnly后,通过JavaScript脚本将无法读取到Cookie信息,这能有效防止用XSS发起攻击
一般来说,跟登录相关的Cookie必须设置为HttpOnly
Cookie的分类与位置
类别 | 描述 |
---|---|
会话Cookie | 临时的Cookie,记录了用户访问站点时的设置与偏好(例如访问本站时的Cookie),关闭浏览器后将被删除 |
持久Cookie | 存储在硬盘上,有过期时间。不管退出浏览器还是重启计算机都存在 |
网站的自动登录就是储存持久Cookie,在用户再次访问相同网站时会先在硬盘中查找相关Cookie然后放到HTTP请求报文中发送给服务器
那么持久Cookie存在计算机那里呢?不同浏览器会在各自的独立空间存放Cookie,互不干扰
例如Linux下Firefox的Cookie位置:~/.mozilla/firefox/xxxxxxxx.default-release/cookies.sqlite
HTTP基本认证
HTTP协议是无状态的,浏览器和Web服务器之间可以通过Cookie来识别身份。那么一些桌面应用程序是如何跟Web服务器之间识别身份呢?
一些网站和Web服务使用的是HTTP基本认证。有些桌面应用程序u也通过HTTP协议跟Web服务器交互,桌面应用程序一般不使用Cookie,而是把用户名+冒号+密码用Base64编码放在HTTP请求Header中的Authorization发送给服务端,这种方式叫HTTP基本认证(Basic Authentication)
在基本认证中,Web服务器可以拒绝一个事物,要求客户端提供有效的用户名和密码,服务器会返回401状态码来初始化认证质询,并用WWW-Authenticate响应首部指定要访问的安全域。浏览器收到质询时,会打开一个对话框请求用户输入用户名和密码,然后将用户名和密码用Base64编码,再用Authorization请求首部发送给服务器
一般家用路由器就是使用基本认证,RESTful API就经常使用基本认证,使用命令curl -u username:password URI
即可完成基本认证
HTTP基本认证的缺点
- HTTP协议是无状态的,同一个客户端对服务器的每个请求都需要认证
- Base64编码是可逆的,非常容易破解,所以基本认证相当于以明文的方式传输用户名和密码。所以基本认证一定要用HTTPS加密传输,稍微安全一点
- 使用基本认证登录后,除非关闭浏览器或清除历史记录,否则无法登出。而Cookie机制的话,网站可以提供登出方式以使Cookie失效
- 无法防止重放攻击
摘要认证
摘要认证是针对基本认证存在的诸多问题而进行改良的方案。摘要认证是另一种HTTP认证协议,它试图修复基本认证的严重缺陷,进行如下改进
- 通过传递用户名、密码等计算出来的摘要来解决以明文方式在网络上发送密码的问题
- 通过服务器产生随机数nonce的方式防止恶意用户捕获并重放认证的握手过程
- 通过客户端产生随机数cnonce的方式支持客户端对服务器的认证
- 通过对内容也加入摘要计算的方式,可以有选择地防止对报文内容的篡改
参考文章
《图解HTTP》读后总结及浅谈 – yexca|Hiyoung‘Blog