写点什么

七、《图解 HTTP》- HTTP 首部和 HTTP 协作服务器

作者:懒时小窝
  • 2022 年 8 月 12 日
    广东
  • 本文字数:12432 字

    阅读完需:约 41 分钟

七、《图解HTTP》- HTTP首部和HTTP协作服务器

#tjhttp 七、《图解 HTTP》- HTTP 首部和 HTTP 协作服务器

#知识点


  1. 请求头部字段分类比较多,本章介绍了下面的首部,内容非常多,熟悉常见的请求首部即可。

  2. 首部字段介绍

  3. 非 HTTP1.1 首部字段

  4. 通用首部

  5. 请求首部

  6. 响应首部

  7. 负载首部(实体首部)

  8. 其他首部字段

  9. 协作服务器指的是为了 HTTP 加速访问而架设的一些中间件介绍,内容介绍比较匮乏,个人也没有补充,简单浏览即可



7-1. HTTP 首部

虽然平时感受不到,但是却是互联网天天在用的东西,这本书花了 50 多页的内容介绍它,可见它的重要性。


HTTP 首部包含三个部分,报文首部,空行和报文主体,报文首部包含了客户端重要的传输信息,而报文体则是“负荷数据”,包含获取服务器信息需要传递的数据。



HTTP 报文由方法、URI、HTTP 版本、HTTP 首部字段等部分构成。



下面是请求报文的案例信息:


GET / HTTP/1.1Host: hackr.jpUser-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*; q=0.8Accept-Language: ja,en-us;q=0.7,en;q=0.3Accept-Encoding: gzip, deflateDNT: 1Connection: keep-aliveIf-Modified-Since: Fri, 31 Aug 2007 02:02:20 GMTIf-None-Match: "45bae1-16a-46d776ac"Cache-Control: max-age=0
复制代码


响应报文结构如下:



响应报文内容:


HTTP/1.1 304 Not ModifiedDate: Thu, 07 Jun 2012 07:21:36 GMTServer: ApacheConnection: closeEtag: "45bae1-16a-46d776ac"
复制代码

7.0 首部字段介绍

首部字段是 HTTP 的重要组成部分。


HTTP 首部字段结构


首部字段由 key/value 的字段名和字段值组成,通过冒号进行分隔,字段值可以是单个值,也可以是多个值,对于多个值会使用逗号进行分隔。


如果首部字段出现重叠怎么办?在规范当中并没有进行明确规定,取决于浏览器和实现方是如何处理的,比如有些浏览器会优先处理第一次出现的首部字段,而有些则会优先处理最后出现的首部字段。


首部字段分类


  • 通用首部字段(General Header Fields):请求和响应通用首部。

  • 请求首部字段(Request Header Fields):从客户端向服务器端发送请求报文时使用的首部。

  • 响应首部字段(Response Header Fields):从服务器端向客户端返回响应报文时使用的首部。

  • 负载(实体)首部字段(Entity Header Fields):在负载的部分使用的首部信息,客户端和服务端都有可能存在。


HTTP/1.1 首部字段


下面是几张关于首部字段的表,包首部字段分类对应的四个分类:


通用首部字段



请求首部字段



响应首部字段



负载首部字段


7.1 非 HTTP/1.1 首部字段

在 HTTP 协议通信中使用的首部字段除了上面定义的之外,非正式的首部字段统一归纳在 RFC4229 HTTP Header Field Registrations 中,感兴趣可以直接进网页看看相关的白皮书信息。


缓存代理行为


缓存代理行为通过两个字段:端到端首部(End-to-end Header)逐跳首部(Hop-by-hop Header)


对于第一个端到端首部(End-to-end Header)会转发请求和响应信息给最终目标并且必须存在于由缓存生成的响应,要求是同时必须被转发。


第二个逐跳首部(Hop-by-hop Header)则只对单次转发有效,如果通过了缓存或者代理则不会进行转发。另外使用逐跳首部需提供 Connection 首部字段需要包含下面的内容:


ConnectionKeep-AliveProxy-AuthenticateProxy-AuthorizationTrailerTETransfer-EncodingUpgrade
复制代码

7.2 通用首部字段

通用首部字段信息包含下面的内容:

7.2.1 Cache-Control

顾名思义,用于操作缓存的首部字段,案例Cache-Control: private, max-age=0, no-cache,缓存首部字段基本存在下面的值,需要指定最大响应 age 和缓存最大的有效时间,防止缓存过久有效和过短失效。


缓存请求指令表响应指令参考表如下:




public 指令(Cache-Control: public)


Cache-Control: public,这样的首部声明表明其他的用户也可以使用这份缓存,意味着这是公用的缓存信息。


private 指令(Cache-Control: private)


Cache-Control: private 和 public 命令正好相反,只能给特定用户作为对象,缓存服务器会为特定的用户缓存数据,其他用户则没用此行为。


no-cache 指令(Cache-Control: no-cache)


目的是为了防止从缓存中返回过期的资源。表示每次请求将不会接受缓存过的数据,如果请求中携带这个指令表明返回的内容不能是缓存过的数据。


注意⚠️:从字面意思上很容易把 no-cache 误解成为不缓存,但事实上 no-cache 代表不缓存过期的资源,缓存会向源服务器进行有效期确认后处理资源。


Cache-Control: no-cache=Location


如果在 cache-Control 当中指定具体的参数值,则客户端接收到这个被指定参数值的首部对应报文之后就不能缓存,这个指令的区别是由服务器指定客户端不允许进行缓存操作。


控制可执行缓存的对象的指令


no-store 指令(Cache-Control: no-store)


​ 表示请求或者响应有机密信息。该指令规定缓存不能在本地存储请求或响应的任一部分。


s-maxage 指令(Cache-Control: s-maxage=604800(单位 :秒)


​ 和 max-age 指令相同,它们的不同点是 s-maxage 指令只适用于供多位用户使用的公共缓存服务器,同一个用户重复返回响应此字段是无效的。


注意⚠️:使用 s-maxage 之后会忽略Expire字段。


max-age 指令


​ 客户端:指定接受最大缓存时间的资源,高于该时间的资源不接受缓存数据,如果为 0 则表示每次都需要请求源服务器。


max-stale 指令(Cache-Control: max-stale=3600(单位:秒))


​ max-stale 指示缓存资源,过期也要照常接受。如果指令没有指定参数值,客户端会接收响应。如果指定参数即使过期,只要处于这个指定值之内依然可以被客户端接收。


only-if-cached 指令(Cache-Control: only-if-cached)


​ 表示只在缓存服务器上获取目标服务器被缓存的资源,如果缓存服务器也没有数据则返回 504 状态码


504 网关超时:服务器充当网关或者代理的时候,没有收到响应。和 408 的区别是 408 是服务端接受客户端超时,504 是代理接收服务端超时。


must-revalidate 指令(Cache-Control: must-revalidate)


​ 表示代理会向源服务器再次验证即将返回的响应缓存目前是否仍然有效,如果是无效的,要求缓存服务器返回 504 的状态码。


注意⚠️:must-revalidate 指令会忽略请求的 max-stale 指令。


proxy-revalidate 指令(Cache-Control: proxy-revalidate)


​ 要求所有缓存服务器收到客户端带有指令的请求返回响应之前验证缓存有效性。



no-transform 指令(Cache-Control: no-transform)


​ 请求和响应不能接受改变负载的媒体类型。


Cache-Control 扩展 cache-extension token Cache-Control: private, community="UCI" 这种写法表示通过 token 标记扩展改首部字段的命令, 比如community这个指令是不存在的,但是通过这样的扩展实现兼容。但是这种兼容只能是理解它的缓存服务器才会回应,其他的缓存服务器会直接忽略掉。

7.2.2 Connection

这个首部字段的作用如下:


  • 控制不转发给代理的首部字段。

  • 管理持久连接。


控制不再转发给代理的字段


​ 可控制不再转发给代理的首部字段(即 Hop-by-hop 首部)。


管理持久连接


​ 如果当服务器端想明确断开连接时,通过指定 Connection 首部字段的值为 Close 完成这项操作。但是需要注意 HTTP1.1 默认都是Keep-Alive 的持久连接。


​ 反之,在此之前的版本都是非持久的连接,如果想要实现和 HTTP1.1 一样的效果需要Connection:Keep-Alive 完成这项操作。

7.2.3 Date(Date: Tue, 03 Jul 2012 04:40:59 GMT)

​ 表明 HTTP 报文创建的日期和时间。


​ HTTP/1.1 协议默认会使用在 RFC1123 中规定的日期时间的格式:


Date: Tue, 03 Jul 2012 04:40:59 GMT


​ HTTP1.1 之前的版本使用下面的内容,使用的协议是 RFC850,主要内容如下所示:


Date: Tue, 03-Jul-12 04:40:59 GMT


​ 除此之外还有一种方式是使用 C 标准库内的 asctime() 函数的输出格式一致:


Date: Tue Jul 03 04:40:59 2012

7.2.3 Pragma(Pragma: no-cache)

Pragma 是 HTTP/1.1 之前版本的历史遗留字段,为了 HTTP1.0 之后向后兼容,规范的内容形式唯一而存在着,比如下面的内容:Pragma: no-cache


主要用于客户端告知服务器不接受缓存内容,这种字段和Cache-Control:no-cache指定缓存处理最为理想。


Cache-Control: no-cachePragma: no-cache
复制代码

7.2.4 Trailer(Trailer: Expires)

表明报文主体之后记录了什么样的首部字段,主要用于 HTTP1.1 的分块传输编码使用。


HTTP/1.1 200 OKDate: Tue, 03 Jul 2012 04:40:56 GMTContent-Type: text/html...Transfer-Encoding: chunkedTrailer: Expires...(报文主体)...0Expires: Tue, 28 Sep 2004 23:59:59 GMT
复制代码


上面的案例使用了 Expires 字段指定资源的失效日期。

7.2.5 Transfer-Encoding(Transfer-Encoding: chunked)

规定传输报文的时候使用的编码方式,HTTP1.1 的传输编码只能够作用于分块传输编码。

7.2.6 Upgrade

表示尝试使用更高版本的协议和服务器之间进行通信,但是不一定是 HTTP 协议,可以指定完全不同的协议。



书中的例子使用了 TLS 的协议仅限验证,注意传输报文的细节部分,比如 Connection 里面指定了 Upgrade,能够产生作用范围的是客户端以及相邻的服务器,所以需要指定Connection: Upgrade 才能生效。


另外服务遇到带有 Upgrade 的请求,可以使用返回码 101 作为响应码返回。


Upgrade 经典使用场景是 WebSocket 升级协议。

7.2.7 Via

主要用于最终客户端到服务器之间的请求和响应报文到传输路径,报文经过了代理和网关时候,会在 Via 当中附加服务器信息然后再进行转发。首部字段 Via 不仅用于追踪报文的转发,还可避免请求回环的发生。



请求每一次经过代理服务器,首部的 Via 字段就会增加一次,VIa 字段用于追踪传播路径,通常会和TRACE方法一起使用,如果Max-Forward变为 0,则会停止代理服务器之间的转发操作。

7.2.8 Warning

HTTP/1.1 的 Warning 首部是从 HTTP/1.0 的响应首部(Retry-After)演变过来的。


下面是对应的组成格式:


Warning: [警告码][警告的主机:端口号]“[警告内容]”([日期时间])
复制代码


在 HTTP1.1 中定义了 7 种警告码,警告码通常只能作为参考,之后可能进行扩展。


7.3 请求首部字段

请求首部是客户端传递给服务端的字段。


7.3.1 Accept(Accept: text/html,application/xhtml+xml,application/xml;q=0.)

首部字段可以 通知服务器,用户代理能够处理的媒体类型以及媒体类型相对优先级。


  • 文本文件

  • text/html, text/plain, text/css ...

  • application/xhtml+xml, application/xml ...

  • 图片文件

  • image/jpeg, image/gif, image/png ...

  • 视频文件

  • video/mpeg, video/quicktime ...

  • 应用程序使用的二进制文件

  • application/octet-stream, application/zip ...


案例:


比如使用 type/subtype 这种形式,一次指定多种媒体类型,通过q=?指定权重值,默认权重为 1,可以设置权重为三位小数。假设服务器可以一次性提供多种信息,会优先提供权重值最高的媒体类型数据。

7.3.2 Accept-Charset(Accept-Charset: iso-8859-5, unicode-1-1;q=0.8)

主要作用是用来通知服务器用户代理支持的字符集及字符集的相对优先顺序,与首部字段 Accept 相同的是,可用权重 q 值来表示相对优先级。


这个字段的主要作用是内容协商机制的服务器驱动协商

7.3.3 Accept-Encoding(Accept-Encoding: gzip, deflate)

主要作用是告知服务器用户代理支持的请求编码以及优先级顺序,支持一次性指定多级编码,编码的相关案例如下:


gzip:由文件压缩程序 gzip(GNU zip)生成的编码格式 (RFC1952),采用 Lempel-Ziv 算法(LZ77)及 32 位循环冗余 校验(Cyclic Redundancy Check,通称 CRC)。


compress:由 UNIX 文件压缩程序 compress 生成的编码格式,采用 Lempel-Ziv-Welch 算法(LZW)。


deflate:组合使用 zlib 格式(RFC1950)及由 deflate 压缩算法(RFC1951)生成的编码格式。


identity:不执行压缩或不会变化的默认编码格式。


注意也可以使用 q=?表示权重值,含义和 Accept 的效果一致,最后注意使用*号作为通配符。

7.3.4 Accept-Language(Accept-Language: zh-cn,zh;q=0.7,en-us,en;q=0.3)

主要作用是告知服务器用户代理支持的自然语言集以及优先级顺序,支持一次性指定多级语言级。


同样也可以使用 q=?表示权重值,按照支持语言排序返回最终支持的语言集即为结果。

7.3.5 Authorization(Authorization: Basic dWVub3NlbjpwYXNzd29yZA==)

和名字一样主要作用是告知服务器的用户认证信息,这个请求首部常常用于接口对接和开发,通常对于没有权限的用户会返回 401 的返回码,告知没有权限访问服务器。

7.3.6 Expect(Expect: 100-continue)

客户端告知服务器某种期望行为使用,但是如果服务器无法理解客户端回应的时候会返回 417 摆烂。客户端利用这个字段表明自己的期望。但是 HTTP1.1 实际上只指明了Expect: 100-continue,表示状态码响应为 100 的客户端需要指定这个字段。


417 表示期望失败


HTTP/1.1 协议里设计 100 (Continue) HTTP 状态码的的目的是,在客户端发送 Request Message 之前,HTTP/1.1 协议允许客户端先判定服务器是否愿意接受客户端发来的消息主体(基于 Request Headers)。


主要针对的情况是如果客户端要给服务器传递一个的数据包,但是如果服务器无法处理或者拒绝处理,这个字段类似提前做好通知。


这个字段的含义其实是让 HTTP1.X 加入了“状态”, 不过这种状态严格意义上不能算作标准,所以 HTTP1.X 依然是无状态的。

7.3.7 From

表示用户代理的邮件地址。注意有时候电子邮件地址因为代理的关系会被记录在 User-Agent 首部字段。

7.3.8 Host(Host: www.hackr.jp

Host 首部字段在 HTTP/1.1 规范内是唯一一个必须被包含在请求内的首部字段。


表示请求方所处的 IP 地址和端口号信息。


为什么必须要有 Host 首部?这和单台服务器分配多个域名的虚拟主机的工作机制有很密切的关联。

7.3.9 If-Match


这样带If前缀的请求首部字段,都是条件请求,服务器接收到附带条件之后需要判定为真才能执行请求。



如上图所示只有if-matchEtag值进行匹配的时候,服务器才会接受请求,如果不符合则返回 412 的响应状态码。另外可以使用星号忽略掉Etag的值,只要有资源就接受。

7.3.10 If-Modified-Since(If-Modified-Since: Thu, 15 Apr 2004 00:00:00 GMT)

如果资源晚于这个字段指定的时间,则希望服务器可以处理资源请求,反之如果资源时间没有过变更则需要返回 304 的响应。


If-Modified-Since 用于确认代理或客户端拥有的本地资源的有效性

7.3.11 If-None-Match

If-Match刚好相反,只有在Etag值和If-None-Match的值不一样的时候才处理请求,这个方法的作用是在 GET 和 HEAD 请求中获取实时信息,类似首部字段 If-Modified-Since

7.3.12 Proxy-Authorization(Proxy-Authorization: Basic dGlwOjkpNLAGfFY5)

通过代理服务器返回过来的质询请求包含了客户端的认证,与客户端以及服务器之间的 HTTP 认证是类似的。

7.3.13 Range(Range: bytes=5001-10000)

首部 Range 可以告知服务器资源指定范围,上面的字节包含 5001 到 10000 字节的资源内容。


如果可以处理相关请求,则返回 206 Partial Content 的响应,如果不能则正常的返回 200。


206 Partial Content:服务器仅发送资源的一部分。

7.3.14 Referer(Referer: http://www.hackr.jp/index.htm

首部字段 Referer 会告知服务器请求的原始资源的 URI。


注意原始资源的 URL 可能包含 ID 和密码等一些敏感信息,如果写入到 Reffer 传给其他服务器有可能泄密。


Referer 的正确的拼写应该是 Referrer,原因大概是老美当初设计的时候觉得单词更加难读吧。

7.3.15 TE(TE: gzip, deflate;q=0.5)

表示服务器客户端能够处理响应的编码方式以及优先级,和 Accept-Encoding 字段类似,但是主要用于传输编码。还可以指定TE: trailers 进行分块传输编码。

7.3.16 User-Agent(User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64;)

User-Agent 用于传达浏览器的种类,首部字段会把创建请求浏览器和用户代理信息传给服务器处理。

7.4 响应首部字段

​ 响应首部字段指的是从服务器端向客户端返回响应报文时使用的首部。


7.4.1 Accept-Ranges(Accept-Ranges: bytes)

当不能处理范围请求时,需要指定Accept-Ranges: none


主要告知客户端服务器能处理的请求范围,比如指定为 Byte 处理字节。

7.4.2 Age(Age: 600)

表示源服务器多久之前创建了响应,字段值为秒。如果创建响应式缓存服务器,则此时间为 Age 缓存之后响应再次发起认证到认证完成的时间。而代理服务器则需要加上首部字段 Age

7.4.3 ETag(ETag: "82e22293907ce725faf67773957acd12")

能告知客户端的负载标志,以一种可以将资源作为字符串形式的唯一标识方式。服务器会给给每个资源分配Etag,另外需要注意资源更新需要和Etag一样保持更新。


所以Etag被用来区分 URI 相同但是语言不同的访问区分不同的访问资源,另外Etag存在强弱之分,强Etag会在资源改动的时候立刻刷新,而弱Etag则在资源改变之后在资源头部加入W/的标识标识资源变更。

7.4.4 Location(Location: http://www.usagid...)

用于表示响应接收方引导到某个和请求 URL 位置不同的资源上面。同样会配合3xx Redirction 重定向返回,几乎所有的浏览器收到这个字段会尝试完成资源重定向的行为。

7.4.5 Proxy-Authenticate(Proxy-Authenticate: Basic realm="Usagidesign Auth")

首部字段Proxy-Authenticate 会通过代理服务器要求的认证信息发给客户端,注意和服务器以及客户端之间的 HTTP 访问认证不同,这是代理服务器和客户端之间的认证。

7.4.6 Retry-After(Retry-After: 120)

此字段表示多久之后可以进行请求重试,配合状态码 503 使用,或者配合 3XX Redirect 一起使用。字段值可以是数字也可以是具体的日期时间,也可以是创建响应之后的秒数。

7.4.7 Server(Server: Apache/2.2.17 (Unix))

告知客户端当前服务器的应用程序信息,可能包含软件版本号信息等。

7.4.8 Vary(Vary: Accept-Language)

表示指定资源请求的时候如果使用Accept-language字段的内容相同则直接从缓存返回响应,否则需要从源服务器仅限返回。


所以这个字段适用于控制缓存,源服务器会给代理服务器传递本地缓存使用方法和调用命令。


如果想要获取缓存则需要和包含Vary字段内容指定的请求才能获取,所以哪怕本次请求和上一次完全相同,请求只要 Vary 不一致,还是需要从源服务器获取。

7.4.9 WWW-Authenticate(WWW-Authenticate: Basic realm="Usagidesign Auth")

主要用于 HTTP 访问认证,告知客户端适用于访问请求指定资源的认证方式,如果返回 401 响应码,则此字段会一并进行返回。注意案例这里的Basic realm="Usagidesign Auth"用于指明资源受到的保护策略。


401 未授权:客户端访问请求的资源需要授权。响应内容中需要包含www-Authnticate 头信息和询问信息,如果已经存在证书访问还是 401 说明证书已经不被接受,如果 401 和前一个身份验证请求相同,并且浏览器进行了至少一次重试,则浏览器应该展示响应包含的实体信息(也就是诊断信息)。

7.5 负载首部字段

因为 HTTP2.0 新协议的缘故,这里更想要称之为负载首部,实体首部的概念已经被废弃。负载首部表明了实体内容的请求头部信息,可以认为是快递上面快递单的货物信息。


7.5.1 Allow(Allow: GET, HEAD)

​ 通知客户端指定资源所有的 HTTP 方法。如果不支持会返回 405 响应。


405 Method Not Allowed:服务器已接收并识别请求,但拒绝了特定的请求方法。该响应必须返回一个 Allow 头信息用以表示出当前资源能够接受的请求方法的列表。 对于一些修改服务器资源数据的请求方法比如 PUT 和 DELETE 通常不被允许。

7.5.2 Content-Encoding(Content-Encoding: gzip)

表明服务器使用的负载的主体部分的内容编码方式,并且在不丢失内容的前提下进行压缩。


主要支持的编码方式如下:


  • gzip

  • compress

  • deflate

  • identity

7.5.3 Content-Language(Content-Language: zh-CN)

告知客户端服务器使用的语言主体。

7.5.4 Content-Length(Content-Length: 15000)

告知实体主体部分大小(单位字节),但是一旦使用内容编码方式传输则不能使用此字段。


可参考 https://tools.ietf.org/html/rfc7231 的 4.4 了解编码格式的内容长度计算。

7.5.5 Content-Location(Content-Location: http://www.hackr.jp/index-ja.html

给出与报文负载部分相对应的 URI,这个字段表示的是报文负载返回资源对应 URI。


比如出现在 Accept-Language 字段实际的 URI 和返回的 URI 可能会不一样,则需要在此字段中标记。

7.5.6 Content-MD5(Content-MD5: OGFkZDUwNGVhNGY3N2MxMDIwZmQ4NTBmY2IyTY==)

客户端对于接受的报文负载内容进行 MD5 加密,目的是保证报文传输的时候保持完整性。


但是需要注意对于报文负载 MD5 加密之后还需要进行 Base64 加密,这是因为 HTTP 首部不能记录二进制的内容,当报文被接受之后同样使用 MD5 算法解密,并且对于负载内容校验完整 。


但是需要注意的是这个字段在校验完整性的同时是无法校验 MD5 加密是否被篡改的,所以安全性保证不佳。

7.5.7 Content-Range(Content-Range: bytes 5001-10000/10000)

告知客户端作为响应返回的负载哪个部分符合范围请求,告知哪一部分符合请求,字段值的单位为字节,表示当前发送部分以及整个实体大小。

7.5.8 Content-Type(Content-Type: text/html; charset=UTF-8)

说明了负载主体内对象的媒体类型,和首部字段 Accept一样,字段值用 type/subtype 形式赋值。


参数 charset 使用 iso-8859-1euc-jp 等字符集进行赋值。

7.5.9 Expires

首部字段 Expires 会将资源失效的日期告知客户端。如果不希望资源被缓存,则在首部字段里面和首部字段 Date 相同。


需要注意在 Cache-Control 指定 max-age 的指令时候,比起首部字段 Expires,会优先处理 max-age 处理

7.5.10 Last-Modified(Last-Modified: Wed, 23 May 2012 09:59:55 GMT)

Last-Modified 指明资源最终修改的时间, 实际通过Request-URI 指定资源被修改的时间。实际案例是在使用 CGI 进行动态数据处理的时候有可能改变这个时间。

7.6 Cookie 服务的首部字段

Cookie 虽然并不是 HTTP1.1 的规范,但是由于在 WEB 领域应用广泛。Cookie 的基本作用是保存用户的访问信息以及状态管理,同时把一些数据写入到客户端可以在下一次访问的时候简化用户操作同时可以减少服务端的一些压力。

7.6.1 Cookie(Cookie: status=enable)

这个首部字段会告知服务器想要获得 HTTP 状态支持管理,这时候请求的时候会包含多个 Cookie 同时可以按照 Cookie 发送。


对于正规发布的 Cookie 而言,由于可以校验有效期、发送方的域名和路径、协议信息等,所以不会受到外来攻击比较安全。


这里顺带说说 Cookie 的历史,Cookie 最初是由于网景公司开发并且制定标准的,但是在后续发展中出现了下面的协议规格:


  • 网景标准(实际标准)

  • 1994 年前后发布,目前普及的标准基本为这个时候的范本,网景的标准是由一个 24 岁的大神写的 5 页纸决定的,目前无法找到任何有关的规范链接,可以参考 RFC6265 看到一些最初的端倪。

  • RFC2109(搞事小弟 1 号)

  • 比较意外这是 W3C 发布的一项标准,本意是想要和网景制定的标准兼容(实则想要取代),但是因为标准过于严苛,同时很多服务实现方错误的实现这个标准,所以后来依然改回了网景的标准。

  • RFC 2109 - HTTP State Management Mechanism (ietf.org)

  • https://www.w3.org/Protocols/rfc2109/rfc2109.txt

  • RFC2965(搞事小弟 2 号)

  • RFC2965 定义了 Cookie2,并试图解决 RFC2109 关于 Cookie1 的缺点。RFC2965 目标在取代 RFC2109。

  • 发送 RFC2965 Cookie 的服务器除了使用 Set-Cookie 标头外,还将使用 Set-Cookie2 标头。注意 RFC2965 Cookie 对端口非常敏感。

  • RFC2965 可在 http://www.w3.org/Protocols/rfc2965/rfc2965.txt,但是实际上属于 W3C 黑历史被删除,

  • 最后通过:RFC 2965 - HTTP State Management Mechanism (ietf.org) 可以阅读了解

  • 然而不幸的是 W3C 还是没成功,因为基本没用多少服务器投入使用。

  • RFC6265:W3C 最后放弃了争夺标准,RFC6265 是按照网景的标准重新定义标准的产物,最终为业界事实标准。(继承大哥,统合一切)

  • 但是结果依然是没有采用 RFC 任何一个协议,网景公司的标准。

  • 从结果来看我们可以认为 RFC6265 是一个先实现后补写设计文档的一种标准,RFC6265 虽然并不是实际采用的标准,但是却是白皮书公开认可的标准规范,也就是从原本大家口头协商变成了白纸黑字的标准的区别。

  • RFC 6265 - HTTP State Management Mechanism (ietf.org)

  • 吐槽:所以符合市场的标准才能被大众接受,哪怕是 W3C 这样庞大的组织也无法撼动一个被认可的标准。


最后特别感谢一下 IETF,可以说是互联网的图书馆,也可以说是互联网发展的基石。另外 RFC 一些被 W3C 掩盖的黑历史也被找到了,哈哈。


IETF 是由网民自发组织,自我管理的,任何人都可以参加的,完全民主平等的,无投票机制的,充分体现了自由、开放、合作、共享的精神)里成立了特别工作小组。


Cookie 的首部字段样式如下:


7.6.2 Set-Cookie

基本的格式如下,在开始使用 Cookie 之前的一些准备操作:


Set-Cookie: status=enable; expires=Tue, 05 Jul 2011 07:26:31 
复制代码


基本的字段属性如下:



expires 属性:发送 Cookie 的有效期,默认为会话为 Seesion 级别,也就是一次浏览器访问。另外需要注意 Cookie 一旦创建服务端就没办法随便删除,只能覆盖的方式改写客户端的 Cookie 信息。


path 属性:限制指定 Cookie 的发送范围目录,但是实际上有办法绕过这个限制,所以这个属性不是一个安全属性。


domain 属性:通过 domain 校验结尾匹配,实际上不指定这个属性更加安全,因为这个属性类似白名单允许多个 domain 访问。


secure 属性(Set-Cookie: name=value; secure):限制仅在 HTTPS 连接才发送 Cookie,是一种比较安全的属性,意味着当同样的域名在使用 HTTPS 的情况下会发送 Cookie,但是转为 HTTP 则不会覆盖客户端的 Cookie。另一方面不指定这个属性意味着不会发生回收行为。

7.6.3 HttpOnly 属性

介绍:属于 Cookie 本身的扩展功能,作用是防止 JS 脚本窃取 Cookie 信息,也就是防止 XSS 攻击。


声明方式:


Set-Cookie: name=value; HttpOnly
复制代码


通过这样的声明之后,JavaScriptdocument.cookie 就无法读取附加 HttpOnly Cookie 的内容了。


实际上 HttpOnly 这个扩展本意并不是为了防止 XSS 攻击发明的,但是后来作为缓解 XSS 攻击的一项重要手段被广泛采用。


XSS 攻击类似下面的脚本:


http://example.jp/login?ID="> <script>var+f=document.getElementById("login");+f.action="h </script><span+s=" 对请求时对应的HTML源代码(摘录)
复制代码

7.6.4 Cookie(Cookie: status=enable)

首部字段 Cookie 会告知服务器,当客户端想获得 HTTP 状态管理支持时,就会在请求中包含从服务器接收到的 Cookie。Cookie 可以发送多个。

7.7 其他首部字段

其他首部字段也是 HTTP 对于开放扩展的支持,这些字段并不符合 WEB 的标准,需要交由实现方决定,但是使用频率并不低。

7.7.1 X-Frame-Options

此字段为响应首部的内容,主要作用是控制 Frame 标签显示内容,主要为了防止点击劫持的攻击方式。


可选内容有下面两项


  • DENY:拒绝

  • SAMEORIGIN:同源页面匹配许可。


主流浏览器基本已经支持这个字段,下面为 Apach 的一个参考:


<IfModule mod_headers.c>Header append X-FRAME-OPTIONS "SAMEORIGIN"</IfModule>
复制代码

7.7.2 X-XSS-Protection(X-XSS-Protection: 1)

首部字段 X-XSS-Protection 属于 HTTP 响应首部,主要作用是用于控制浏览器 XSS 防护机制的开关。


语法:


X-XSS-Protection: 0X-XSS-Protection: 1X-XSS-Protection: 1; mode=blockX-XSS-Protection: 1; report=<reporting-uri>
复制代码


标识解释:


  • 0:禁止 XSS 过滤。

  • 1:启用 XSS 过滤(通常浏览器是默认的)。 如果检测到跨站脚本攻击,浏览器将清除页面(删除不安全的部分)。

  • 1;mode=block,启用 XSS 过滤。 如果检测到攻击,浏览器将不会清除页面,而是阻止页面加载。

  • 1; report=<reporting-URI> (Chromium only),启用 XSS 过滤。 如果检测到跨站脚本攻击,浏览器将清除页面并使用 CSP report-uri (en-US)指令的功能发送违规报告。

7.7.2 DNT

DNT 属于 HTTP 请求首部,是 Do Not Track 的简 称,主要用于防止广告抓取个人信息。


首部字段 DNT 可指定的字段值如下。


  • 0 :同意被追踪

  • 1 :拒绝被追踪


这里介绍一个好用的谷歌插件“Ublock origin”,图标类似一个小红色盾牌。

最大特点可以利用 html 元素直接抹掉页面的广告信息过滤元素,非常好用。

7.7.3 P3P

P3P(The Platform for Privacy Preferences,在线隐私偏好平台)技术,通过这个首部可以把隐私信息变为仅应用程序识别的方式处理。


创建 P3P 的步骤如下:


步骤 1:创建 P3P 隐私。


步骤 2:创建 P3P 隐私对照文件后,保存命名在 /w3c/p3p.xml。


步骤 3:从 P3P 隐私中新建 Compact policies 后,输出到 HTTP 响应中。


关于 P3P 可以继续阅读下面的内容:


The Platform for Privacy Preferences 1.0(P3P1.0)Specification http://www.w3.org/TR/P3P/


X-前缀废弃:通过这个前缀来排查掉非标准参数,并且依次作为非标准参数的扩展,但是实际使用发现这样不仅导致命名混乱,还可能影响正常的通信,所以在后续的“RFC 6648 - Deprecating the "X-" Prefix and Similar Constructs in Application Protocols”废弃此用法。

7-2. HTTP 协作服务器

7.1 单台虚拟机多域名

HTTP1.1 支持服务器搭建多个站点,提供 WEB 托管服务, 而针对域名和 IP 的映射以及查找工作涉及到 DNS,域名需要通过 DNS 解析之后才能进行访问,当请求发送到服务器的时候使用的已经是 IP 的方式了。

7.2 通信转发程序

通信转发存在几个专业术语:代理、网关、隧道,下面一一区分他们的概念。


代理:代理扮演了服务端和客户端的“中间商”,代理服务器的基本行为就是接收客户端发送的请求后转发给其他服务器。代理的作用通常是加快目标站点的访问加速或者作为跳板使用。


网关:专门负责转发其他服务器的通信数据的服务器,对于自己的位置类似传话筒,负责把一个服务器的“话”传给另一个服务器,所以发送请求的服务器本身也会被当作被转发的服务器。


隧道:保证距离很远的客户端和服务器中转的应用程序。

7.2.1 代理

代理主要的变动信息在 Via 首部信息,每次代理转发都需要在 Via 首部加入转发信息,具体添加信息如下:



对于代理按照是否修改报文和是否缓存数据,分为透明代理缓存代理


  • 透明代理:透明代理指的是不对请求报文做任何加工的代理方式。

  • 缓存代理:缓存代理通常存在于缓存服务器,代理转发响应之前先把数据缓存到缓存服务器,然后再进行返回到客户端。

7.2.2 缓存服务器

缓存服务器的作用是减轻服务器的负担,利用缓存可以避免同样的资源反复从源服务器进行返回,而可以直接从缓存服务器获取资源。这部分内容在《网络是怎么样连接的》这本书中有详细介绍。

7.2.3 隧道

隧道可按要求建立起一条与其他服务器的通信线路,届时使用 SSL 等 加密手段进行通信。


HTTP 之前出现的协议


  • FTP:比 TCP/IP 协议族的出现还要早,虽然被 HTTP 超越,但是目前还是还是广泛用于文件上传。

  • NNTP(Network News Transfer Protocol):用于 NetNews 电子会议室内传送消息的协议。

  • Archie:搜索 anonymous FTP 公开的文件信息的协议。

  • WAIS(Wide Area Information Servers):通过关键词检索多个数据库使用的协议。

  • Gopher:查找与互联网连接的计算机内信息的协议。


用户头像

懒时小窝

关注

赐他一块白石,石头上写着新名 2020.09.23 加入

如果我们想要知道自己想要做什么,必须先找到自己的白色石头。欢迎关注个人公众号“懒时小窝”,不传播焦虑,只分享和思考有价值的内容。

评论

发布
暂无评论
七、《图解HTTP》- HTTP首部和HTTP协作服务器_HTTP_懒时小窝_InfoQ写作社区