图解 HTTP
上野宣
1.1 使用 HTTP 协议访问 Web
你知道当我们在网页浏览器(Web browser)的地址栏中输入 URL 时,Web 页面是如何呈现的吗?
像这种通过发送请求获取服务器资源的 Web 浏览器等,都可称为客户端(client)。
Web 是建立在 HTTP 协议上通信的。
1.2 HTTP 的诞生
现在已提出了 3 项 WWW 构建技术,分别是:把 SGML(Standard Generalized Markup Language,标准通用标记语言)作为页面的文本标记语言的 HTML(HyperText Markup Language,超文本标记语言);作为文档传递协议的 HTTP;指定文档所在地址的 URL(Uniform Resource Locator,统一资源定位符)。
当年 HTTP 协议的出现主要是为了解决文本传输的难题。
1.3 网络基础 TCP/IP
通常使用的网络(包括互联网)是在 TCP/IP 协议族的基础上运作的。而 HTTP 属于它内部的一个子集。
把与互联网相关联的协议集合起来总称为 TCP/IP。也
TCP/IP 协议族按层次分别分为以下 4 层:应用层、传输层、网络层和数据链路层。
应用层 应用层决定了向用户提供应用服务时通信的活动。 TCP/IP 协议族内预存了各类通用的应用服务。比如,FTP(File Transfer Protocol,文件传输协议)和 DNS(Domain Name System,域名系统)服务就是其中两类。 HTTP 协议也处于该层。 传输层 传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据传输。 在传输层有两个性质不同的协议:TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报协议)。 网络层(又名网络互连层) 网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数据单位。该层规定了通过怎样的路径(所谓的传输路线)到达对方计算机,并把数据包传送给对方。 与对方计算机之间通过多台计算机或网络设备进行传输时,网络层所起的作用就是在众多的选项内选择一条传输路线。 链路层(又名数据链路层,网络接口层) 用来处理连接网络的硬件部分。包括控制操作系统、硬件的设备驱动、NIC(Network Interface Card,网络适配器,即网卡),及光纤等物理可见部分(还包括连接器等一切传输媒介)。硬件上的范畴均在链路层的作用范围之内。
发送端在层与层之间传输数据时,每经过一层时必定会被打上一个该层所属的首部信息。反之,接收端在层与层传输数据时,每经过一层时会把对应的首部消去。 这种把数据信息包装起来的做法称为封装(encapsulate)。
1.4 与 HTTP 关系密切的协议:IP、TCP 和 DNS
按层次分,IP(Internet Protocol)网际协议位于网络层。
可能有人会把“IP”和“IP 地址”搞混,“IP”其实是一种协议的名称。 IP 协议的作用是把各种数据包传送给对方。而要保证确实传送到对方那里,则需要满足各类条件。其中两个重要的条件是 IP 地址和 MAC 地址(Media Access Control Address)。 IP 地址指明了节点被分配到的地址,MAC 地址是指网卡所属的固定地址。IP 地址可以和 MAC 地址进行配对。IP 地址可变换,但 MAC 地址基本上不会更改。
使用 ARP 协议凭借 MAC 地址进行通信 IP 间的通信依赖 MAC 地址。在网络上,通信的双方在同一局域网(LAN)内的情况是很少的,通常是经过多台计算机和网络设备中转才能连接到对方。而在进行中转时,会利用下一站中转设备的 MAC 地址来搜索下一个中转目标。这时,会采用 ARP 协议(Address Resolution Protocol)。ARP 是一种用以解析地址的协议,根据通信方的 IP 地址就可以反查出对应的 MAC 地址。
没有人能够全面掌握互联网中的传输状况 在到达通信目标前的中转过程中,那些计算机和路由器等网络设备只能获悉很粗略的传输路线。 这种机制称为路由选择(routing),有点像快递公司的送货过程。
按层次分,TCP 位于传输层,提供可靠的字节流服务。
字节流服务(Byte Stream Service)是指,为了方便传输,将大块数据分割成以报文段(segment)为单位的数据包进行管理。
为了准确无误地将数据送达目标处,TCP 协议采用了三次握手(three-way handshaking)策略。
握手过程中使用了 TCP 的标志(flag)——SYN(synchronize)和 ACK(acknowledgement)。 发送端首先发送一个带 SYN 标志的数据包给对方。接收端收到后,回传一个带有 SYN/ACK 标志的数据包以示传达确认信息。最后,发送端再回传一个带 ACK 标志的数据包,代表“握手”结束。
1.5 负责域名解析的 DNS 服务
DNS(Domain Name System)服务是和 HTTP 协议一样位于应用层的协议。它提供域名到 IP 地址之间的解析服务。
DNS 协议提供通过域名查找 IP 地址,或逆向从 IP 地址反查域名的服务。
1.6 各种协议与 HTTP 协议的关系
通过这张图来了解下 IP 协议、TCP 协议和 DNS 服务在使用 HTTP 协议的通信过程中各自发挥了哪些作用
1.7 URI 和 URL
URI 就是由某个协议方案表示的资源的定位标识符。协议方案是指访问资源所使用的协议类型名称。 采用 HTTP 协议时,协议方案就是 http。除此之外,还有 ftp、mailto、telnet、file 等。
URI 用字符串标识某一互联网资源,而 URL 表示资源的地点(互联网上所处的位置)。可见 URL 是 URI 的子集。
使用绝对 URI 必须指定待访问的服务器地址。地址可以是类似 hackr.jp 这种 DNS 可解析的名称,或是 192.168.1.1 这类 IPv4 地址名,还可以是[0:0:0:0:0:0:0:1]这样用方括号括起来的 IPv6 地址名。
由于不遵照 RFC 标准实现就无法进行 HTTP 协议通信,所以基本上客户端和服务器端都会以 RFC 为标准来实现 HTTP 协议。
2.1 HTTP 协议用于客户端和服务器端之间的通信
请求访问文本或图像等资源的一端称为客户端,而提供资源响应的一端称为服务器端。
而用 HTTP 协议能够明确区分哪端是客户端,哪端是服务器端。
2.2 通过请求和响应的交换达成通信
请求报文是由请求方法、请求 URI、协议版本、可选的请求首部字段和内容实体构成的。
响应报文基本上由协议版本、状态码(表示请求成功或失败的数字代码)、用以解释状态码的原因短语、可选的响应首部字段以及实体主体构成。
2.3 HTTP 是不保存状态的协议
HTTP 是一种不保存状态,即无状态(stateless)协议。HTTP 协议自身不对请求和响应之间的通信状态进行保存。也就是说在 HTTP 这个级别,协议对于发送过的请求或响应都不做持久化处理。
是为了更快地处理大量事务,确保协议的可伸缩性,而特意把 HTTP 协议设计成如此简单的。
用户登录到一家购物网站,即使他跳转到该站的其他页面后,也需要能继续保持登录状态。针对这个实例,网站为了能够掌握是谁送出的请求,需要保存用户的状态。
于是引入了 Cookie 技术
2.4 请求 URI 定位资源
如果不是访问特定资源而是对服务器本身发起请求,可以用一个*来代替请求 URI。
2.5 告知服务器意图的 HTTP 方法
下面,我们介绍 HTTP/1.1 中可使用的方法。 GET:获取资源 GET 方法用来请求访问已被 URI 识别的资源。指定的资源经服务器端解析后返回响应内容。
POST:传输实体主体 POST 方法用来传输实体的主体。 虽然用 GET 方法也可以传输实体的主体,但一般不用 GET 方法进行传输,而是用 POST 方法。
PUT:传输文件 PUT 方法用来传输文件。就像 FTP 协议的文件上传一样,要求在请求报文的主体中包含文件内容,然后保存到请求 URI 指定的位置。 但是,鉴于 HTTP/1.1 的 PUT 方法自身不带验证机制,任何人都可以上传文件,存在安全性问题,因此一般的 Web 网站不使用该方法。若配合 Web 应用程序的验证机制,或架构设计采用 REST(Representational State Transfer,表征状态转移)标准的同类 Web 网站,就可能会开放使用 PUT 方法。
HEAD:获得报文首部 HEAD 方法和 GET 方法一样,只是不返回报文主体部分。用于确认 URI 的有效性及资源更新的日期时间等。
DELETE:删除文件 DELETE 方法用来删除文件,是与 PUT 相反的方法。DELETE 方法按请求 URI 删除指定的资源。 但是,HTTP/1.1 的 DELETE 方法本身和 PUT 方法一样不带验证机制,所以一般的 Web 网站也不使用 DELETE 方法。
OPTIONS:询问支持的方法 OPTIONS 方法用来查询针对请求 URI 指定的资源支持的方法。
2.7 持久连接节省通信量
HTTP 协议的初始版本中,每进行一次 HTTP 通信就要断开一次 TCP 连接。
因此,每次的请求都会造成无谓的 TCP 连接建立和断开,增加通信量的开销。
为解决上述 TCP 连接的问题,HTTP/1.1 和一部分的 HTTP/1.0 想出了持久连接(HTTP Persistent Connections,也称为 HTTP keep-alive 或 HTTP connection reuse)的方法。持久连接的特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。
持久连接的好处在于减少了 TCP 连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。另外,减少开销的那部分时间,使 HTTP 请求和响应能够更早地结束,这样 Web 页面的显示速度也就相应提高了。 在 HTTP/1.1 中,所有的连接默认都是持久连接,但在 HTTP/1.0 内并未标准化。虽然有一部分服务器通过非标准的手段实现了持久连接,但服务器端不一定能够支持持久连接。毫无疑问,除了服务器端,客户端也需要支持持久连接。
持久连接使得多数请求以管线化(pipelining)方式发送成为可能。从前发送请求后需等待并收到响应,才能发送下一个请求。管线化技术出现后,不用等待响应亦可直接发送下一个请求。
2.8 使用 Cookie 的状态管理
不可否认,无状态协议当然也有它的优点。由于不必保存状态,自然可减少服务器的 CPU 及内存资源的消耗。从另一侧面来说,也正是因为 HTTP 协议本身是非常简单的,所以才会被应用在各种场景里。
Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。 Cookie 会根据从服务器端发送的响应报文内的一个叫做 Set-Cookie 的首部字段信息,通知客户端保存 Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入 Cookie 值后发送出去。 服务器端发现客户端发送过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
3.1 HTTP 报文
用于 HTTP 协议交互的信息被称为 HTTP 报文。请求端(客户端)的 HTTP 报文叫做请求报文,响应端(服务器端)的叫做响应报文。HTTP 报文本身是由多行(用 CR+LF 作换行符)数据构成的字符串文本。 HTTP 报文大致可分为报文首部和报文主体两块。两者由最初出现的空行(CR+LF)来划分。通常,并不一定要有报文主体。
3.2 请求报文及响应报文的结构
请求报文和响应报文的首部内容由以下数据组成。
请求行 包含用于请求的方法,请求 URI 和 HTTP 版本。 状态行 包含表明响应结果的状态码,原因短语和 HTTP 版本。 首部字段 包含表示请求和响应的各种条件和属性的各类首部。 一般有 4 种首部,分别是:通用首部、请求首部、响应首部和实体首部。 其他 可能包含 HTTP 的 RFC 里未定义的首部(Cookie 等)。
3.3 编码提升传输速率
HTTP 在传输数据时可以按照数据原貌直接传输,但也可以在传输过程中通过编码提升传输速率。通过在传输时编码,能有效地处理大量的访问请求。但是,编码的操作需要计算机来完成,因此会消耗更多的 CPU 等资源。 3.3.1 报文主体和实体主体的差异 ● 报文(message) 是 HTTP 通信中的基本单位,由 8 位组字节流(octet sequence,其中 octet 为 8 个比特)组成,通过 HTTP 通信传输。 ● 实体(entity) 作为请求或响应的有效载荷数据(补充项)被传输,其内容由实体首部和实体主体组成。 HTTP 报文的主体用于传输请求或响应的实体主体。 通常,报文主体等于实体主体。只有当传输中进行编码操作时,实体主体的内容发生变化,才导致它和报文主体产生差异。
向待发送邮件内增加附件时,为了使邮件容量变小,我们会先用 ZIP 压缩文件之后再添加附件发送。HTTP 协议中有一种被称为内容编码的功能也能进行类似的操作。 内容编码指明应用在实体内容上的编码格式,并保持实体信息原样压缩。内容编码后的实体由客户端接收并负责解码。
常用的内容编码有以下几种。 ●gzip(GNU zip) ●compress(UNIX 系统的标准压缩) ●deflate(zlib) ●identity(不进行编码)
在 HTTP 通信过程中,请求的编码实体资源尚未全部传输完成之前,浏览器无法显示请求页面。在传输大容量数据时,通过把数据分割成多块,能够让浏览器逐步显示页面。 这种把实体主体分块的功能称为分块传输编码(Chunked Transfer Coding)。
分块传输编码会将实体主体分成多个部分(块)。每一块都会用十六进制来标记块的大小,而实体主体的最后一块会使用“0(CR+LF)”来标记。 使用分块传输编码的实体主体会由接收的客户端负责解码,恢复到编码前的实体主体。 HTTP/1.1 中存在一种称为传输编码(Transfer Coding)的机制,它可以在通信时按某种编码方式传输,但只定义作用于分块传输编码中。
3.4 发送多种数据的多部分对象集合
发送邮件时,我们可以在邮件里写入文字并添加多份附件。这是因为采用了 MIME(Multipurpose Internet Mail Extensions,多用途因特网邮件扩展)机制,它允许邮件处理文本、图片、视频等多个不同类型的数据。例如,图片等二进制数据以 ASCII 码字符串编码的方式指明,就是利用 MIME 来描述标记数据类型。而在 MIME 扩展中会使用一种称为多部分对象集合(Multipart)的方法,来容纳多份不同类型的数据。 相应地,HTTP 协议中也采纳了多部分对象集合,发送的一份报文主体内可含有多类型实体。通常是在图片或文本文件等上传时使用。
3.5 获取部分内容的范围请求
如果下载过程中遇到网络中断的情况,那就必须重头开始。为了解决上述问题,需要一种可恢复的机制。所谓恢复是指能从之前下载中断处恢复下载。 要实现该功能需要指定下载的实体范围。像这样,指定范围发送的请求叫做范围请求(Range Request)。
3.6 内容协商返回最合适的内容
当浏览器的默认语言为英语或中文,访问相同 URI 的 Web 页面时,则会显示对应的英语版或中文版的 Web 页面。这样的机制称为内容协商(Content Negotiation)。
内容协商机制是指客户端和服务器端就响应的资源内容进行交涉,然后提供给客户端最为适合的资源。内容协商会以响应资源的语言、字符集、编码方式等作为判断的基准。
4.1 状态码告知从服务器端返回的请求结果
状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。
状态码如 200 OK,以 3 位数字和原因短语组成。 数字中的第一位指定了响应类别,后两位无分类。
只要遵守状态码类别的定义,即使改变 RFC2616 中定义的状态码,或服务器端自行创建状态码都没问题。
附加 HTTP 状态码(RFC6585)等扩展,数量就达 60 余种。别看种类繁多,实际上经常使用的大概只有 14 种。
4.2 2XX 成功
在响应报文内,随状态码一起返回的信息会因方法的不同而发生改变。比如,使用 GET 方法时,对应请求资源的实体会作为响应返回;而使用 HEAD 方法时,对应请求资源的实体主体不随报文首部作为响应返回(即在响应中只返回首部,不会返回实体的主体部分)。
该状态码代表服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分。
状态码表示客户端进行了范围请求,而服务器成功执行了这部分的 GET 请求。响应报文中包含由 Content-Range 指定范围的实体内容。
4.3 3XX 重定向
永久性重定向。该状态码表示请求的资源已被分配了新的 URI,以后应使用资源现在所指的 URI。也就是说,如果已经把资源对应的 URI 保存为书签了,这时应该按 Location 首部字段提示的 URI 重新保存。
临时性重定向。该状态码表示请求的资源已被分配了新的 URI,希望用户(本次)能使用新的 URI 访问。
状态码表示由于请求对应的资源存在着另一个 URI,应使用 GET 方法定向获取请求的资源。 303 状态码和 302 Found 状态码有着相同的功能,但 303 状态码明确表示客户端应当采用 GET 方法获取资源,这点与 302 状态码有区别。
当 301、302、303 响应状态码返回时,几乎所有的浏览器都会把 POST 改成 GET,并删除请求报文内的主体,之后请求会自动再次发送。 301、302 标准是禁止将 POST 方法改变成 GET 方法的,但实际使用时大家都会这么做。
状态码表示客户端发送附带条件的请求 时,服务器端允许请求访问资源,但因发生请求未满足条件的情况后,直接返回 304 Not Modified(服务器端资源未改变,可直接使用客户端未过期的缓存)。304 状态码返回时,不包含任何响应的主体部分。304 虽然被划分在 3XX 类别中,但是和重定向没有关系。
临时重定向。该状态码与 302 Found 有着相同的含义。尽管 302 标准禁止 POST 变换成 GET,但实际使用时大家并不遵守。 307 会遵照浏览器标准,不会从 POST 变成 GET。但是,对于处理响应时的行为,每种浏览器有可能出现不同的情况。
4.4 4XX 客户端错误
状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求。另外,浏览器会像 200 OK 一样对待该状态码。
状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息。另外若之前已进行过 1 次请求,则表示用户认证失败。
该状态码表明对请求资源的访问被服务器拒绝了。服务器端没有必要给出拒绝的详细理由,但如果想作说明的话,可以在实体的主体部分对原因进行描述,这样就能让用户看到了。 未获得文件系统的访问授权,访问权限出现某些问题(从未授权的发送源 IP 地址试图访问)等列举的情况都可能是发生 403 的原因。
状态码表明服务器上无法找到请求的资源。除此之外,也可以在服务器端拒绝请求且不想说明理由时使用。
4.5 5XX 服务器错误
状态码表明服务器端在执行请求时发生了错误。也有可能是 Web 应用存在的 bug 或某些临时的故障。
状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。如果事先得知解除以上状况需要的时间,最好写入 Retry-After 首部字段再返回给客户端。
状态码和状况的不一致 不少返回的状态码响应都是错误的,但是用户可能察觉不到这点。比如 Web 应用程序内部发生错误,状态码依然返回 200 OK,这种情况也经常遇到。
5.1 用单台虚拟主机实现多个域名
HTTP/1.1 规范允许一台 HTTP 服务器搭建多个 Web 站点。比如,提供 Web 托管服务(
即使物理层面只有一台服务器,但只要使用虚拟主机的功能,则可以假想已具有多台服务器。
在相同的 IP 地址下,由于虚拟主机可以寄存多个不同主机名和域名的 Web 网站,因此在发送 HTTP 请求时,必须在 Host 首部内完整指定主机名或域名的 URI。
5.2 通信数据转发程序:代理、网关、隧道
HTTP 通信时,除客户端和服务器以外,还有一些用于通信数据转发的应用程序,例如代理、网关和隧道。
代理是一种有转发功能的应用程序,它扮演了位于服务器和客户端“中间人”的角色,接收由客户端发送的请求并转发给服务器,同时也接收服务器返回的响应并转发给客户端。
网关是转发其他服务器通信数据的服务器,接收从客户端发送来的请求时,它就像自己拥有资源的源服务器一样对请求进行处理。有时客户端可能都不会察觉,自己的通信目标是一个网关。
隧道是在相隔甚远的客户端和服务器两者之间进行中转,并保持双方通信连接的应用程序
在 HTTP 通信过程中,可级联多台代理服务器。请求和响应的转发会经过数台类似锁链一样连接起来的代理服务器。转发时,需要附加 Via 首部字段以标记出经过的主机信息。
使用代理服务器的理由有:利用缓存技术(稍后讲解)减少网络带宽的流量,组织内部针对特定网站的访问控制,以获取访问日志为主要目的,等等。 代理有多种使用方法,按两种基准分类。一种是是否使用缓存,另一种是是否会修改报文。
代理转发响应时,缓存代理(Caching Proxy)会预先将资源的副本(缓存)保存在代理服务器上。
转发请求或响应时,不对报文做任何加工的代理类型被称为透明代理(Transparent Proxy)。反之,对报文内容进行加工的代理被称为非透明代理。
网关的工作机制和代理十分相似。而网关能使通信线路上的服务器提供非 HTTP 协议服务。 利用网关能提高通信的安全性,因为可以在客户端与网关之间的通信线路上加密以确保连接的安全。比如,网关可以连接数据库,使用 SQL 语句查询数据。另外,在 Web 购物网站上进行信用卡结算时,网关可以和信用卡结算系统联动。
隧道可按要求建立起一条与其他服务器的通信线路,届时使用 SSL 等加密手段进行通信。隧道的目的是确保客户端能与服务器进行安全的通信。 隧道本身不会去解析 HTTP 请求。也就是说,请求保持原样中转给之后的服务器。隧道会在通信双方断开连接时结束。
5.3 保存资源的缓存
缓存是指代理服务器或客户端本地磁盘内保存的资源副本。利用缓存可减少对源服务器的访问,因此也就节省了通信流量和通信时间。 缓存服务器是代理服务器的一种,并归类在缓存代理类型中。
即使存在缓存,也会因为客户端的要求、缓存的有效期等因素,向源服务器确认资源的有效性。若判断缓存失效,缓存服务器将会再次从源服务器上获取“新”资源。
把客户端缓存称为临时网络文件(Temporary Internet File)。 浏览器缓存如果有效,就不必再向服务器请求相同的资源了,可以直接从本地磁盘内读取。 另外,和缓存服务器相同的一点是,当判定缓存过期后,会向源服务器确认资源的有效性。
6.1 HTTP 报文首部
HTTP 协议的请求和响应报文中必定包含 HTTP 首部。首部内容为客户端和服务器分别处理请求和响应提供所需要的信息。对于客户端用户来说,这些信息中的大部分内容都无须亲自查看。
在请求中,HTTP 报文由方法、URI、HTTP 版本、HTTP 首部字段等部分构成。
在响应中,HTTP 报文由 HTTP 版本、状态码(数字和原因短语)、HTTP 首部字段 3 部分构成。
6.2 HTTP 首部字段
起到传递额外重要信息的作用。 使用首部字段是为了给浏览器和服务器提供报文主体大小、所使用的语言、认证信息等内容。
当 HTTP 报文首部中出现了两个或两个以上具有相同首部字段名时会怎么样?这种情况在规范内尚未明确,根据浏览器内部处理逻辑的不同,结果可能并不一致。有些浏览器会优先处理第一次出现的首部字段,而有些则会优先处理最后出现的首部字段。
通用首部字段(General Header Fields) 请求报文和响应报文两方都会使用的首部。 请求首部字段(Request Header Fields) 从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、响应内容相关优先级等信息。 响应首部字段(Response Header Fields) 从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息。 实体首部字段(Entity Header Fields) 针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的信息。
HTTP 首部字段将定义成缓存代理和非缓存代理的行为,分成 2 种类型。 端到端首部(End-to-end Header) 分在此类别中的首部会转发给请求/响应对应的最终接收目标,且必须保存在由缓存生成的响应中,另外规定它必须被转发。 逐跳首部(Hop-by-hop Header) 分在此类别中的首部只对单次转发有效,会因通过缓存或代理而不再转发。HTTP/1.1 和之后版本中,如果要使用 hop-by-hop 首部,需提供 Connection 首部字段。 下面列举了 HTTP/1.1 中的逐跳首部字段。除这 8 个首部字段之外,其他所有字段都属于端到端首部。 ●Connection ●Keep-Alive ●Proxy-Authenticate ●Proxy-Authorization ●Trailer ●TE ●Transfer-Encoding ●Upgrade
6.3 HTTP/1.1 通用首部字段
通过指定首部字段 Cache-Control 的指令,就能操作缓存的工作机制。
当使用 no-store 指令 时,暗示请求(和对应的响应)或响应中包含机密信息。 因此,该指令规定缓存不能在本地存储请求或响应的任一部分。
Connection 首部字段具备如下两个作用。 ● 控制不再转发给代理的首部字段 ● 管理持久连接
首部字段 Date 表明创建 HTTP 报文的日期和时间。
Pragma 是 HTTP/1.1 之前版本的历史遗留字段,仅作为与 HTTP/1.0 的向后兼容而定义。
该首部字段属于通用首部字段,但只用在客户端发送的请求中。客户端会要求所有的中间服务器不返回缓存的资源。
首部字段 Trailer 会事先说明在报文主体后记录了哪些首部字段。该首部字段可应用在 HTTP/1.1 版本分块传输编码时。
首部字段 Transfer-Encoding 规定了传输报文主体时采用的编码方式。
首部字段 Upgrade 用于检测 HTTP 协议及其他协议是否可使用更高的版本进行通信,其参数值可以用来指定一个完全不同的通信协议。
使用首部字段 Via 是为了追踪客户端与服务器之间的请求和响应报文的传输路径。
首部字段 Via 不仅用于追踪报文的转发,还可避免请求回环的发生。所以必须在经过代理时附加该首部字段内容。
HTTP/1.1 的 Warning 首部是从 HTTP/1.0 的响应首部(Retry-After)演变过来的。该首部通常会告知用户一些与缓存相关的问题的警告。
6.4 请求首部字段
请求首部字段是从客户端往服务器端发送请求报文中所使用的字段,用于补充请求的附加信息、客户端信息、对响应内容相关的优先级等内容。
Accept 首部字段可通知服务器,用户代理能够处理的媒体类型及媒体类型的相对优先级。可使用 type/subtype 这种形式,一次指定多种媒体类型。
若想要给显示的媒体类型增加优先级,则使用 q=来额外表示权重值 ,用分号(;)进行分隔。权重值 q 的范围是 0 ~ 1(可精确到小数点后 3 位),且 1 为最大值。不指定权重 q 值时,默认权重为 q=1.0。 当服务器提供多种内容时,将会首先返回权重值最高的媒体类型。
Accept-Charset 首部字段可用来通知服务器用户代理支持的字符集及字符集的相对优先顺序。另外,可一次性指定多种字符集。与首部字段 Accept 相同的是可用权重 q 值来表示相对优先级。 该首部字段应用于内容协商机制的服务器驱动协商。
Accept-Encoding 首部字段用来告知服务器用户代理支持的内容编码及内容编码的优先级顺序。可一次性指定多种内容编码。
首部字段 Accept-Language 用来告知服务器用户代理能够处理的自然语言集(指中文或英文等),以及自然语言集的相对优先级。可一次指定多种自然语言集。
首部字段 Authorization 是用来告知服务器,用户代理的认证信息(证书值)。
客户端使用首部字段 Expect 来告知服务器,期望出现的某种特定行为。因服务器无法理解客户端的期望作出回应而发生错误时,会返回状态码 417 Expectation Failed。
首部字段 From 用来告知服务器使用用户代理的用户的电子邮件地址
首部字段 Host 会告知服务器,请求的资源所处的互联网主机名和端口号。Host 首部字段在 HTTP/1.1 规范内是唯一一个必须被包含在请求内的首部字段。 首部字段 Host 和以单台服务器分配多个域名的虚拟主机的工作机制有很密切的关联,这是首部字段 Host 必须存在的意义。
形如 If-xxx 这种样式的请求首部字段,都可称为条件请求。服务器接收到附带条件的请求后,只有判断指定条件为真时,才会执行请求。
If-Modified-Since 用于确认代理或客户端拥有的本地资源的有效性。获取资源的更新日期时间,可通过确认首部字段 Last-Modified 来确定。
首部字段 If-Range 属于附带条件之一。它告知服务器若指定的 If-Range 字段值(ETag 值或者时间)和请求资源的 ETag 值或时间相一致时,则作为范围请求处理。反之,则返回全体资源。
接收到附带 Range 首部字段请求的服务器,会在处理请求之后返回状态码为 206 Partial Content 的响应。无法处理该范围请求时,则会返回状态码 200 OK 的响应及全部资源。
首部字段 Referer 会告知服务器请求的原始资源的 URI。
首部字段 TE 会告知服务器客户端能够处理响应的传输编码方式及相对优先级。它和首部字段 Accept-Encoding 的功能很相像,但是用于传输编码。
首部字段 User-Agent 会将创建请求的浏览器和用户代理名称等信息传达给服务器。
6.5 响应首部字段
响应首部字段是由服务器端向客户端返回响应报文中所使用的字段,用于补充响应的附加信息、服务器信息,以及对客户端的附加要求等信息。
Accept-Ranges 是用来告知客户端服务器是否能处理范围请求,以指定获取服务器端某个部分的资源。 可指定的字段值有两种,可处理范围请求时指定其为 bytes,反之则指定其为 none。
首部字段 Age 能告知客户端,源服务器在多久前创建了响应。字段值的单位为秒。 若创建该响应的服务器是缓存服务器,Age 值是指缓存后的响应再次发起认证到认证完成的时间值。代理创建响应时必须加上首部字段 Age。
首部字段 ETag 能告知客户端实体标识。它是一种可将资源以字符串形式做唯一性标识的方式。服务器会为每份资源分配对应的 ETag 值。
首部字段 Proxy-Authenticate 会把由代理服务器所要求的认证信息发送给客户端。
首部字段 Retry-After 告知客户端应该在多久之后再次发送请求。主要配合状态码 503 Service Unavailable 响应,或 3xx Redirect 响应一起使用。
首部字段 Server 告知客户端当前服务器上安装的 HTTP 服务器应用程序的信息。不单单会标出服务器上的软件应用名称,还有可能包括版本号和安装时启用的可选项。
首部字段 Vary 可对缓存进行控制。源服务器会向代理服务器传达关于本地缓存使用方法的命令。
首部字段 WWW-Authenticate 用于 HTTP 访问认证。它会告知客户端适用于访问请求 URI 所指定资源的认证方案(Basic 或是 Digest)和带参数提示的质询(challenge)。
6.6 实体首部字段
实体首部字段是包含在请求报文和响应报文中的实体部分所使用的首部,用于补充内容的更新时间等与实体相关的信息。
首部字段 Allow 用于通知客户端能够支持 Request-URI 指定资源的所有 HTTP 方法。
首部字段 Content-Encoding 会告知客户端服务器对实体的主体部分选用的内容编码方式。内容编码是指在不丢失实体信息的前提下所进行的压缩。
首部字段 Content-Language 会告知客户端,实体主体使用的自然语言(指中文或英文等语言)。
首部字段 Content-Length 表明了实体主体部分的大小(单位是字节)。对实体主体进行内容编码传输时,不能再使用 Content-Length 首部字段。由于实体主体大小的计算方法略微复杂,所以在此不再展开。
首部字段 Content-Location 给出与报文主体部分相对应的 URI。和首部字段 Location 不同,Content-Location 表示的是报文主体返回资源对应的 URI。
首部字段 Content-MD5 是一串由 MD5 算法生成的值,其目的在于检查报文主体在传输过程中是否保持完整,以及确认传输到达。
由于 HTTP 首部无法记录二进制值,所以要通过 Base64 编码处理。
针对范围请求,返回响应时使用的首部字段 Content-Range,能告知客户端作为响应返回的实体的哪个部分符合范围请求。字段值以字节为单位,表示当前发送部分及整个实体大小。
首部字段 Content-Type 说明了实体主体内对象的媒体类型。和首部字段 Accept 一样,字段值用 type/subtype 形式赋值。
首部字段 Expires 会将资源失效的日期告知客户端。缓存服务器在接收到含有首部字段 Expires 的响应后,会以缓存来应答请求,在 Expires 字段值指定的时间之前,响应的副本会一直被保存。
首部字段 Last-Modified 指明资源最终修改的时间。
6.7 为 Cookie 服务的首部字段
Cookie 的工作机制是用户识别及状态管理。
下面的表格列举了 Set-Cookie 的字段值。
Cookie 的 expires 属性指定浏览器可发送 Cookie 的有效期
一旦 Cookie 从服务器端发送至客户端,服务器端就不存在可以显式删除 Cookie 的方法。但可通过覆盖已过期的 Cookie,实现对客户端 Cookie 的实质性删除操作。
Cookie 的 secure 属性用于限制 Web 页面仅在 HTTPS 安全连接时,才可以发送 Cookie。
Cookie 的 HttpOnly 属性是 Cookie 的扩展功能,它使 JavaScript 脚本无法获得 Cookie。其主要目的为防止跨站脚本攻击(Cross-site scripting,XSS)对 Cookie 的信息窃取。
7.1 HTTP 的缺点
HTTP 主要有这些不足,例举如下。 ● 通信使用明文(不加密),内容可能会被窃听 ● 不验证通信方的身份,因此有可能遭遇伪装 ● 无法证明报文的完整性,所以有可能已遭篡改
如果通信经过加密,就有可能让人无法破解报文信息的含义,但加密处理后的报文信息本身还是会被看到的
HTTP 协议中没有加密机制,但可以通过和 SSL(Secure Socket Layer,安全套接层)或 TLS(Transport Layer Security,安全传输层协议)的组合使用,加密 HTTP 的通信内容。
● 无法确定请求发送至目标的 Web 服务器是否是按真实意图返回响应的那台服务器。有可能是已伪装的 Web 服务器。 ● 无法确定响应返回到的客户端是否是按真实意图接收响应的那个客户端。有可能是已伪装的客户端。 ● 无法确定正在通信的对方是否具备访问权限。因为某些 Web 服务器上保存着重要的信息,只想发给特定用户通信的权限。 ● 无法判定请求是来自何方、出自谁手。 ● 即使是无意义的请求也会照单全收。无法阻止海量请求下的 DoS 攻击(Denial of Service,拒绝服务攻击)。
SSL 不仅提供加密处理,而且还使用了一种被称为证书的手段,可用于确定方。 证书由值得信任的第三方机构颁发,用以证明服务器和客户端是实际存在的。
完整性是指信息的准确度。若无法证明其完整性,通常也就意味着无法判断信息是否准确。
请求或响应在传输途中,遭攻击者拦截并篡改内容的攻击称为中间人攻击(Man-in-the-Middle attack,MITM)。
虽然有使用 HTTP 协议确定报文完整性的方法,但事实上并不便捷、可靠。其中常用的是 MD5 和 SHA-1 等散列值校验的方法,以及用来确认文件的数字签名方法。
SSL 提供认证和加密处理及摘要功能。仅靠 HTTP 确保完整性是非常困难的,因此通过和其他协议组合使用来实现这个目标。下节我们介绍 HTTPS 的相关内容。
7.2 HTTP+加密+认证+完整性保护=HTTPS
HTTP 协议通信过程中使用未经加密的明文,比如在 Web 页面中输入信用卡号,如果这条通信线路遭到窃听,那么信用卡号就暴露了。 另外,对于 HTTP 来说,服务器也好,客户端也好,都是没有办法确认通信方的。因为很有可能并不是和原本预想的通信方在实际通信。并且还需要考虑到接收到的报文在通信途中已经遭到篡改这一可能性。 为了统一解决上述这些问题,需要在 HTTP 上再加入加密处理和认证等机制。我们把添加了加密及认证机制的 HTTP 称为 HTTPS(HTTP Secure)。
所谓 HTTPS,其实就是身披 SSL 协议这层外壳的 HTTP。
SSL 是独立于 HTTP 的协议,所以不光是 HTTP 协议,其他运行在应用层的 SMTP 和 Telnet 等协议均可配合 SSL 协议使用。可以说 SSL 是当今世界上应用最为广泛的网络安全技术。
■ 共享密钥加密的困境 加密和解密同用一个密钥的方式称为共享密钥加密(Common key crypto system),也被叫做对称密钥加密。
■ 使用两把密钥的公开密钥加密 公开密钥加密方式很好地解决了共享密钥加密的困难。
使用公开密钥加密方式,发送密文的一方使用对方的公开密钥进行加密处理,对方收到被加密的信息后,再使用自己的私有密钥进行解密。利用这种方式,不需要发送用来解密的私有密钥,也不必担心密钥被攻击者窃听而盗走。
在交换密钥环节使用公开密钥加密方式,之后的建立通信交换报文阶段则使用共享密钥加密方式。
遗憾的是,公开密钥加密方式还是存在一些问题的。那就是无法证明公开密钥本身就是货真价实的公开密钥。
或许在公开密钥传输途中,真正的公开密钥已经被攻击者替换掉了。 为了解决上述问题,可以使用由数字证书认证机构(CA,Certificate Authority)和其相关机关颁发的公开密钥证书。 数字证书认证机构处于客户端与服务器双方都可信赖的第三方机构的立场上。
TSL 是以 SSL 为原型开发的协议,有时会统一称该协议为 SSL。当前主流的版本是 SSL3.0 和 TLS1.0。
HTTPS 也存在一些问题,那就是当使用 SSL 时,它的处理速度会变慢。
图:HTTPS 比 HTTP 要慢 2 到 100 倍
SSL 的慢分两种。一种是指通信慢。另一种是指由于大量消耗 CPU 及内存等资源,导致处理速度变慢。 和使用 HTTP 相比,网络负载可能会变慢 2 到 100 倍。除去和 TCP 连接、发送 HTTP 请求·响应以外,还必须进行 SSL 通信,因此整体上处理通信量不可避免会增加。
8.1 何为认证
核对的信息通常是指以下这些。 ● 密码:只有本人才会知道的字符串信息。 ● 动态令牌:仅限本人持有的设备内显示的一次性密码。 ● 数字证书:仅限本人(终端)持有的信息。 ● 生物认证:指纹和虹膜等本人的生理信息。 ●IC 卡等:仅限本人持有的信息。
8.2 BASIC 认证
BASIC 认证(基本认证)是从 HTTP/1.0 就定义的认证方式。即便是现在仍有一部分的网站会使用这种认证方式。是 Web 服务器与通信客户端之间进行的认证方式。
8.3 DIGEST 认证
DIGEST 认证同样使用质询/响应的方式(challenge/response),但不会像 BASIC 认证那样直接发送明文密码。 所谓质询响应方式是指,一开始一方会先发送认证要求给另一方,接着使用从另一方那接收到的质询码计算生成响应码。最后将响应码返回给对方进行认证的方式。
DIGEST 认证提供了高于 BASIC 认证的安全等级,但是和 HTTPS 的客户端认证相比仍旧很弱。DIGEST 认证提供防止密码被窃听的保护机制,但并不存在防止用户伪装的保护机制。 DIGEST 认证和 BASIC 认证一样,使用上不那么便捷灵活,且仍达不到多数 Web 网站对高度安全等级的追求标准。因此它的适用范围也有所受限。
8.4 SSL 客户端认证
从使用用户 ID 和密码的认证方式方面来讲,只要二者的内容正确,即可认证是本人的行为。但如果用户 ID 和密码被盗,就很有可能被第三者冒充。利用 SSL 客户端认证则可以避免该情况的发生。 SSL 客户端认证是借由 HTTPS 的客户端证书完成认证的方式。凭借客户端证书(在 HTTPS 一章已讲解)认证,服务器可确认访问是否来自已登录的客户端。
8.5 基于表单认证
无法实现状态管理,因此即使当该用户下一次继续访问,也无法区分他与其他的用户。于是我们会使用 Cookie 来管理 Session,以弥补 HTTP 协议中不存在的状态管理功能。
步骤 3: 客户端接收到从服务器端发来的 Session ID 后,会将其作为 Cookie 保存在本地。下次向服务器发送请求时,浏览器会自动发送 Cookie,所以 Session ID 也随之发送到服务器。服务器端可通过验证接收到的 Session ID 识别用户和其认证状态。 除了以上介绍的应用实例,还有应用其他不同方法的案例
一种安全的保存方法是,先利用给密码加盐(salt) 的方式增加额外信息,再使用散列(hash)函数计算出散列值后保存。
9.2 消除 HTTP 瓶颈的 SPDY
使用 HTTP 协议探知服务器上是否有内容更新,就必须频繁地从客户端到服务器端进行确认。如果服务器上没有内容更新,那么就会产生徒劳的通信。 若想在现有 Web 实现所需的功能,以下这些 HTTP 标准就会成为瓶颈。 ● 一条连接上只可发送一个请求。 ● 请求只能从客户端开始。客户端不可以接收除响应以外的指令。 ● 请求/响应首部未经压缩就发送。首部信息越多延迟越大。 ● 发送冗长的首部。每次互相发送相同的首部造成的浪费较多。 ● 可任意选择数据压缩格式。非强制压缩发送。
Ajax 的核心技术是名为 XMLHttpRequest 的 API,通过 JavaScript 脚本语言的调用就能和服务器进行 HTTP 通信。借由这种手段,就能从已加载完毕的 Web 页面上发起请求,只更新局部页面。
Comet 的解决方法 一旦服务器端有内容更新了,Comet 不会让请求等待,而是直接给客户端返回响应。这是一种通过延迟应答,模拟实现服务器端向客户端推送(Server Push)的功能。
内容上虽然可以做到实时更新,但为了保留响应,一次连接的持续时间也变长了。期间,为了维持连接会消耗更多的资源。另外,Comet 也仍未解决 HTTP 协议本身存在的问题。
SPDY 没有完全改写 HTTP 协议,而是在 TCP/IP 的应用层与传输层之间通过新加会话层的形式运作。同时,考虑到安全性问题,SPDY 规定通信中使用 SSL。 SPDY 以会话层的形式加入,控制对数据的流动,但还是采用 HTTP 建立通信连接。因此,可照常使用 HTTP 的 GET 和 POST 等方法、Cookie 以及 HTTP 报文等
9.3 使用浏览器进行全双工通信的 WebSocket
WebSocket,即 Web 浏览器与 Web 服务器之间全双工通信标准。
仍在开发中的 WebSocket 技术主要是为了解决 Ajax 和 Comet 里 XMLHttpRequest 附带的缺陷所引起的问题。
由于是建立在 HTTP 基础上的协议,因此连接的发起方仍是客户端,而一旦确立 WebSocket 通信连接,不论服务器还是客户端,任意一方都可直接向对方发送报文
10.1 HTML
HTML(HyperText Markup Language,超文本标记语言)是为了发送 Web 上的超文本(Hypertext)而开发的标记语言。超文本是一种文档系统,可将文档中任意位置的信息与其他信息(文本或图片等)建立关联,即超链接文本。标记语言是指通过在文档的某部分穿插特别的字符串标签,用来修饰文档的语言。我们把出现在 HTML 文档内的这种特殊字符串叫做 HTML 标签(Tag)。 平时我们浏览的 Web 页面几乎全是使用 HTML 写成的。由 HTML 构成的文档经过浏览器的解析、渲染后,呈现出来的结果就是 Web 页面。
CSS(Cascading Style Sheets,层叠样式表)可以指定如何展现 HTML 内的各种元素,属于样式表标准之一。即使是相同的 HTML 文档,通过改变应用的 CSS,用浏览器看到的页面外观也会随之改变。CSS 的理念就是让文档的结构和设计分离,达到解耦的目的。
10.2 动态 HTML
所谓动态 HTML(Dynamic HTML),是指使用客户端脚本语言将静态的 HTML 内容变成动态的技术的总称。鼠标单击点开的新闻、Google Maps 等可滚动的地图就用到了动态 HTML。 动态 HTML 技术是通过调用客户端脚本语言 JavaScript,实现对 HTML 的 Web 页面的动态改造。利用 DOM(Document Object Model,文档对象模型)可指定欲发生动态变化的 HTML 元素
DOM 是用以操作 HTML 文档和 XML 文档的 API(Application Programming Interface,应用编程接口)。使用 DOM 可以将 HTML 内的元素当作对象操作,如取出元素内的字符串、改变那个 CSS 的属性等,使页面的设计发生改变
10.3 Web 应用
CGI(Common Gateway Interface,通用网关接口)是指 Web 服务器在接收到客户端发送过来的请求后转发给程序的一组机制。
Servlet 是一种能在服务器上创建动态内容的程序。Servlet 是用 Java 语言实现的一个接口,属于面向企业级 Java(JavaEE,Java Enterprise Edition)的一部分。
10.4 数据发布的格式及语言
JSON(JavaScript Object Notation)是一种以 JavaScript(ECMAScript)的对象表示法为基础的轻量级数据标记语言。能够处理的数据类型有 false/null/true/对象/数组/数字/字符串,这 7 种类型。
11.1 针对 Web 的攻击技术
整体上看,HTTP 就是一个通用的单纯协议机制。因此它具备较多优势,但是在安全性方面则呈劣势。 就拿远程登录时会用到的 SSH 协议来说,SSH 具备协议级别的认证及会话管理等功能,HTTP 协议则没有。
主动攻击模式里具有代表性的攻击是 SQL 注入攻击和 OS 命令注入攻击。
11.2 因输出值转义不完全引发的安全漏洞
SQL 注入是攻击者将 SQL 语句改变成开发者意想不到的形式以达到破坏结构的攻击。