5. 与 HTTP 协作的 Web 服务器
一台 Web 服务器可搭建多个独立域名的 Web 网站,也可作为通信路径上的中转服务器提升传输效率。
5.1 用单台虚拟主机实现多个域名
HTTP1.1 规范允许一个服务器搭建多个 Web 站点,这是虚拟主机(Virtual Host,又称虚拟服务器)功能。
如果一台服务器内托管了两个域名,对应的同一个服务器 IP,当收到请求时就需要弄清楚究竟要访问哪个域名,由于虚拟主机可以寄存多个不同主机名和域名的 Web 网站,因此在发送 HTTP 请求时,必须在 Host
首部内完整指定主机名或域名的 URI。
5.2 通信数据转发程序 :代理、网关、隧道
这些应用程序和服务器可以将请求转发给通信线路上的下一站服务器,并且能接收从那台服务器发送的响应再转发给客户端。
- 代理: 接收客户端发送的请求后转发给其他服务器,。代理不改变请求 URI,会直接发送给前方持有资源的目标服务器。
- 缓存代理:预先将资源缓存保存在代理服务器上,当代理再次接收到对相同资源的请求时,就可以直接将之前缓存的资源作为响应返回。
- 透明代理:转发请求或响应时,不对报文做任何加工被称为透明代理,对报文内容进行加工的称为非透明代理。
- 网关: 转发其他服务器通信数据的服务器,接收从客户端发送来的请求时,它就像自己拥有资源的源服务器一样对请求进行处理。有时客户端可能都不会察觉,自己的通信目标是一个网关。网关能提高通信的安全性,因为可以在客户端与网关之间的通信线路上加密以确保连接的安全。
- 隧道: 按要求建立起一条与其他服务器的通信线路,届时使用 SSL 等加密手段进行通信,在通信双方断开连接时结束。。隧道的目的是确保客户端能与服务器进行安全的通信。
5.3 保存资源的缓存
缓存是指代理服务器或客户端本地保存的资源副本,是代理服务器的一种。
优势在于避免多次从源服务器转发资源,客户端可就近从缓存服务器上获取资源,而源服务器也不必多次处理相同请求。
客户端的缓存: 浏览器缓存如果有效,不必再向服务器请求,而直接从本地读取。当判定缓存过期后,会向源服务器确认资源的有效性。若判断浏览器缓存失效,浏览器会再次请求新资源。
6. HTTP 首部
HTTP 协议的请求和响应报文中必定包含 HTTP 首部,请求报文大约是下面的结构,响应报文类似
6.1 HTTP 首部字段
- 由首部字段名和字段值构成的,中间用冒号“:” 分隔
- 字段值对应单个 HTTP 首部字段可以有多个值
HTTP 首部字段将定义成缓存代理和非缓存代理的行为,分成 2 种类型。
- 端到端首部: 分在此类别中的首部会转发给请求 / 响应对应的最终接收目标,且必须保存在由缓存生成的响应中,另外规定它必须被转发。
- 逐跳首部: 分在此类别中的首部只对单次转发有效,会因通过缓存或代理而不再转发。HTTP1.1 和之后版本中,如果要使用 hop-by-hop 首部,需提供 Connection 首部字段。
下面列举了 HTTP1.1 中的逐跳首部字段。除这 8 个首部字段之外,其他所有字段都属于端到端首部。
- Connection
- Keep-Alive
- Proxy-Authenticate
- Proxy-Authorization
- Trailer
- TE
- Transfer-Encoding
- Upgrade
HTTP 首部根据用途被分为 4 种 HTTP 首部字段类型,在 HTTP 协议通信交互中使用到的首部字段,不限于 RFC2616 中定义的 47 种。还有 Cookie、Set-Cookie 和 Content-Disposition 等在其他 RFC 中定义的,使用频率也很高。
通用首部字段
请求报文和响应报文两方都会使用的首部。
首部字段名 | 说明 |
Cache-Control | 控制缓存的行为 |
Connection | 逐跳首部、连接的管理 |
Date | 创建报文的日期时间 |
Pragma | 报文指令 |
Trailer | 报文末端的首部一览 |
Transfer-Encoding | 指定报文主体的传输编码方式 |
Upgrade | 升级为其他协议 |
Via | 代理服务器的相关信息 |
Warning | 错误通知 |
请求首部字段
从客户端向服务器端发送请求报文时使用的首部。补充了请求的附加内容、客户端信息、响应内容相关优先级等信息。
首部字段名 | 说明 |
Accept | 用户代理可处理的媒体类型 |
Accept-Charset | 优先的字符集 |
Accept-Encoding | 优先的内容编码 |
Accept-Language | 优先的语言(自然语言) |
Authorization | Web认证信息 |
Expect | 期待服务器的特定行为 |
From | 用户的电子邮箱地址 |
Host | 请求资源所在服务器 |
If-Match | 比较实体标记(ETag) |
If-Modified-Since | 比较资源的更新时间 |
If-None-Match | 比较实体标记(与 If-Match 相反) |
If-Range | 资源未更新时发送实体 Byte 的范围请求 |
If-Unmodified-Since | 比较资源的更新时间(与If-Modified-Since相反) |
Max-Forwards | 最大传输逐跳数 |
Proxy-Authorization | 代理服务器要求客户端的认证信息 |
Range | 实体的字节范围请求 |
Referer | 对请求中 URI 的原始获取方 |
TE | 传输编码的优先级 |
User-Agent | HTTP 客户端程序的信息 |
响应首部字段
从服务器端向客户端返回响应报文时使用的首部。补充了响应的附加内容,也会要求客户端附加额外的内容信息。
首部字段名 | 说明 |
Accept-Ranges | 是否接受字节范围请求 |
Age | 推算资源创建经过时间 |
ETag | 资源的匹配信息 |
Location | 令客户端重定向至指定URI |
Proxy-Authenticate | 代理服务器对客户端的认证信息 |
Retry-After | 对再次发起请求的时机要求 |
Server | HTTP服务器的安装信息 |
Vary | 代理服务器缓存的管理信息 |
WWW-Authenticate | 服务器对客户端的认证信息 |
实体首部字段
针对请求报文和响应报文的实体部分使用的首部。补充了资源内容更新时间等与实体有关的信息。
首部字段名 | 说明 |
Allow | 资源可支持的HTTP方法 |
Content-Encoding | 实体主体适用的编码方式 |
Content-Language | 实体主体的自然语言 |
Content-Length | 实体主体的大小(单位:字节) |
Content-Location | 替代对应资源的URI |
Content-MD5 | 实体主体的报文摘要 |
Content-Range | 实体主体的位置范围 |
Content-Type | 实体主体的媒体类型 |
Expires | 实体主体过期的日期时间 |
Last-Modified | 资源的最后修改日期时间 |
6.2 HTTP1.1 通用首部字段
通用首部字段是指请求报文和响应报文都会使用的首部。
Cache-Control
- no-cache: 防止从缓存中返回过期的资源。客户端请求如果包含
no-cache
,表示客户端将不会接收缓存过的响应,缓存服务器必须把客户端请求转发给源服务器。服务器响应中包含no-cache
,那么缓存服务器不能对资源进行缓存,源服务器以后也将不再对缓存服务器请求中提出的资源有效性进行确认,且禁止其对响应资源进行缓存操作。 - no-store: 缓存不能在本地存储请求或响应的任一部分。
从字面意思上很容易把 no-cache
误解成为不缓存,但 no-cache
代表不缓存过期的资源,缓存会向源服务器进行有效期确认后处理资源,no-store 才是真正地不进行缓存。
Connection
- 控制不再转发给代理的首部字段: 在客户端发送请求和服务器返回响应内,使用
Connection
首部字段,可控制不再转发给代理的首部字段(即 Hop-by-hop 首部)。 - 管理持久连接: HTTP1.1 默认持久连接,客户端会在持久连接上连续发送请求。服务器端想断开连接时,则设置
Connection
首部字段为Close
。HTTP1.1 之前默认都是非持久连接。为此,如果想在旧版本 HTTP 协议上持续连接,则需设置Connection
首部字段为Keep-Alive
。
Date
表明创建 HTTP 报文的日期和时间。
Upgrade
用于检测 HTTP 协议及其他协议是否可使用更高的版本进行通信,其参数值可以用来指定一个完全不同的通信协议。
6.3 请求首部字段
从客户端往服务器端发送请求报文中所使用的字段,用于补充请求的附加信息、客户端信息、对响应内容相关的优先级等内容。
Accept
通知服务器,用户代理能够处理的媒体类型及媒体类型的相对优先级。可使用 type/subtype
这种形式,一次指定多种媒体类型。
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 复制代码
Host
告知服务器,请求的资源所处的互联网主机名和端口号。Host 首部字段在 HTTP1.1 规范内是唯一一个必须被包含在请求内的首部字段。
请求被发送至服务器时,主机名会直接用 IP 地址。但如果这时,相同的 IP 地址下部署多个域名,那么服务器就会无法理解究竟是哪个域名对应的请求。因此,就需要使用首部字段 Host
来明确指出请求的主机名。若服务器未设定主机名,那直接设空值即可。
If-Match
形如 If-xxx 这种,都可称为条件请求。服务器接收到后,只有判断指定条件为真时,才会执行请求。
首部字段 If-Match,属附带条件之一,它会告知服务器匹配资源所用的实体标记(ETag)值。这时的服务器无法使用弱 ETag 值。服务器会比对 If-Match 的字段值和资源的 ETag 值,仅当两者一致时,才会执行请求。
可以用星号 *
,服务器会忽略 ETag
的值,只要资源存在就处理请求。
If-None-Match
和 If-Match
作用相反,用于指定 If-None-Match
的实体标记 ETag
值与请求资源的 ETag
不一致时,它就告知服务器处理该请求。
If-Modified-Since
如果在 If-Modified-Since
字段指定的日期时间后资源发生了更新,服务器会接受请求。
指定 If-Modified-Since
字段值的日期时间之后,请求的资源在给定的日期时间之后对内容进行过修改的情况下才会将资源返回,状态码为 200
,如果请求的资源没有更新,则返回状态码 304 Not Modified 的响应。
If-Unmodified-Since
If-Unmodified-Since
和 If-Modified-Since
的作用相反。它的作用的是告知服务器,指定的请求资源只有在指定日期时间之后未发生更新,才处理请求。如果在指定日期时间后发生了更新,则以状态码 412 Precondition Failed 作为响应返回。
If-Range
If-Range
字段如果跟 ETag 值或更新的日期时间一致,那么就作为范围请求处理。反之,则返回全体资源。
6.4 响应首部字段
由服务器端向客户端返回响应报文中所使用的字段,用于补充响应的附加信息、服务器信息,以及对客户端的附加要求等信息。
ETag
实体标识,将资源以字符串形式做唯一性标识的方式。服务器会为每份资源分配对应的 ETag
值。当资源更新时,ETag
值也需要更新。
若在下载过程中出现连接中断、再连接的情况,都会依照 ETag
值来指定资源。
6.5 实体首部字段
包含在请求报文和响应报文中的实体部分所使用的首部,用于补充内容的更新时间等与实体相关的信息。
Allow
通知客户端能够支持的所有 HTTP 方法。当服务器接收到不支持的 HTTP 方法时,会以状态码 405 Method Not Allowed 作为响应返回。与此同时,还会把所有能支持的 HTTP 方法写入首部字段 Allow
后返回。
Content-Encoding
告知客户端服务器对实体的主体部分选用的内容编码方式。内容编码是指在不丢失实体信息的前提下所进行的压缩。
主要有:gzip、compress、deflate、identity
Content-Length
表明了实体主体部分的大小(单位是字节)。对实体主体进行内容编码传输时,不能再使用 Content-Length
首部字段。
Content-Type
说明了实体主体内对象的媒体类型,用 type/subtype 形式赋值。
Content-Type: text/html; charset=UTF-8 复制代码
Expires
Expires
会将资源失效的日期告知客户端。缓存服务器在收到有 Expires
的响应后,会以缓存来应答请求,在 Expires
字段值指定的时间之前,响应的副本会一直被保存。当超过指定的时间后,缓存服务器在请求发送过来时,会转向源服务器请求资源。
源服务器不希望缓存服务器对资源缓存时,最好在 Expires
字段内写入与 Date
相同的时间值。
但是,当首部字段 Cache-Control
有指定 max-age
时,比起 Expires
,会优先处理 max-age
指令。
Last-Modified
包含源头服务器认定的资源做出修改的日期及时间。
6.6 为 Cookie 服务的首部字段
首部字段名 | 说明 | 首部类型 |
Set-Cookie | 开始状态管理所使用的Cookie信息 | 响应首部字段 |
Cookie | 服务器接收到的Cookie信息 | 请求首部字段 |
Set-Cookie
Set-Cookie: status=enable; expires=Tue, 05 Jul 2011 07:26:31 GMT; path=/; domain=.hackr.jp; 复制代码
属性 | 说明 |
NAME=VALUE | 赋予 Cookie 的名称和其值(必需项) |
expires=DATE | Cookie 的有效期(若不明确指定则默认为浏览器关闭前为止) |
path=PATH | 将服务器上的文件目录作为Cookie的适用对象(若不指定则默认为文档所在的文件目录) |
domain=域名 | 作为 Cookie 适用对象的域名 (若不指定则默认为创建 Cookie 的服务器的域名) |
Secure | 仅在 HTTPS 安全通信时才会发送 Cookie |
HttpOnly | 加以限制,使 Cookie 不能被 JavaScript 脚本访问 |
一旦 Cookie 从服务器端发送至客户端,服务器端就没有显式删除 Cookie 的方法。但可通过覆盖已过期的 Cookie,实现对客户端 Cookie 的实质性删除操作。
HttpOnly
属性是 Cookie 的扩展功能,它使 JavaScript 脚本无法获得 Cookie。其主要目的为防止跨站脚本攻击(Cross-site scripting,XSS)对 Cookie 的信息窃取。
Cookie
Cookie: status=enable 复制代码
当客户端想获得 HTTP 状态管理支持时,就会在请求中包含从服务器接收到的 Cookie。
6.7 其他首部字段
X-XSS-Protection
是针对跨站脚本攻击(XSS)的一种对策,用于控制浏览器 XSS 防护机制的开关,可指定的字段值如下
- 0:将 XSS 过滤设置成无效状态
- 1:将 XSS 过滤设置成有效状态
7. 确保 Web 安全的 HTTPS
HTTP 协议中有可能存在信息窃听或身份伪装等安全问题,使用 HTTPS 可以有效地防止这些问题。
7.1 HTTP 的缺点
- 通信使用明文(不加密),内容可能会被窃听
- 不验证通信方的身份,因此有可能遭遇伪装
- 无法证明报文的完整性,所以有可能已遭篡改
7.2 HTTP+ 加密 + 认证 + 完整性保护 = HTTPS
把添加了加密及认证机制的 HTTP 称为 HTTPS(HTTP Secure)。HTTPS 并非是应用层的一种新协议,只是 HTTP 通信接口部分用 SSL(Secure Socket Layer)和 TLS(Transport Layer Security)协议代替而已。SSL 是独立于 HTTP 的协议,所以其他运行在应用层的 SMTP 和 Telnet 等协议均可配合 SSL 协议使用。
HTTPS 采用共享密钥加密和公开密钥加密两者并用的混合加密机制。若密钥能够实现安全交换,那么有可能会考虑仅使用公开密钥加密来通信。但是公开密钥加密与共享密钥加密相比,其处理速度要慢。
所以取长补短,在交换密钥环节使用公开密钥加密方式,之后的建立通信交换报文阶段则使用共享密钥加密方式。
数字证书认证机构(CA,Certificate Authority)和其相关机关颁发的公开密钥证书就是认证的可以信赖的公开密钥,服务器会将这份由数字证书认证机构颁发的公钥证书发送给客户端,以进行公开密钥加密方式通信。公钥证书也可叫做数字证书或直接称为证书。
HTTPS 通信的步骤:
- 客户端通过发送 Client Hello 报文开始 SSL 通信。报文中包含客户端支持的 SSL 的指定版本、加密组件(Cipher Suite)列表(所使用的加密算法及密钥长度等)。
- 服务器可进行 SSL 通信时,会以 Server Hello 报文作为应答。和客户端一样,在报文中包含 SSL 版本以及加密组件。服务器的加密组件内容是从接收到的客户端加密组件内筛选出来的。
- 之后服务器发送 Certificate 报文。报文中包含公开密钥证书。
- 最后服务器发送 Server Hello Done 报文通知客户端,最初阶段的 SSL 握手协商部分结束。
- SSL 第一次握手结束之后,客户端以 Client Key Exchange 报文作为回应。报文中包含通信加密中使用的一种被称为 Pre-master secret 的随机密码串。该报文已用步骤 3 中的公开密钥进行加密。
- 接着客户端继续发送 Change Cipher Spec 报文。该报文会提示服务器,在此报文之后的通信会采用 Pre-master secret 密钥加密。
- 客户端发送 Finished 报文。该报文包含连接至今全部报文的整体校验值。这次握手协商是否能够成功,要以服务器是否能够正确解密该报文作为判定标准。
- 服务器同样发送 Change Cipher Spec 报文。
- 服务器同样发送 Finished 报文。
- 服务器和客户端的 Finished 报文交换完毕之后,SSL 连接就算建立完成。当然,通信会受到 SSL 的保护。从此处开始进行应用层协议的通信,即发送 HTTP 请求。
- 应用层协议通信,即发送 HTTP 响应。
- 最后由客户端断开连接。断开连接时,发送 close_notify 报文。上图做了一些省略,这步之后再发送 TCP FIN 报文来关闭与 TCP 的通信。
在以上流程中,应用层发送数据时会附加一种叫做 MAC(Message Authentication Code)的报文摘要。MAC 能够查知报文是否遭到篡改,从而保护报文的完整性。
SSL 速度慢吗
HTTPS 也存在一些问题,那就是当使用 SSL 时,它的处理速度会变慢。它慢分两种。一种是指通信慢。另一种是指由于大量消耗 CPU 及内存等资源,导致处理速度变慢。
和 HTTP 相比,HTTPS 网络负载可能会变慢 2 到 100 倍。除去和 TCP 连接、发送 HTTP 请求响应以外,还必须进行 SSL 通信,因此整体上处理通信量不可避免会增加。
另外 SSL 必须进行加密处理,在服务器和客户端都需要进行加解密处理,比 HTTP 消耗更多硬件资源,导致负载增强。
针对速度变慢这一问题,并没有根本性的解决方案,一般会使用 SSL 加速器这种(专用服务器)硬件。能提高数倍 SSL 的计算速度,仅在 SSL 处理时发挥功效,分担负载。
没使用 HTTPS 的原因
与纯文本通信相比,加密通信会消耗更多的 CPU 及内存资源。如果每次通信都加密,会消耗相当多的资源,平摊到一台计算机上时,能够处理的请求数量必定也会随之减少。
如果是非敏感信息则使用 HTTP 通信,只有在包含个人信息等敏感数据时,才利用 HTTPS 加密通信。可以仅在那些需要信息隐藏时才加密,以节约资源。
除此之外,想要节约购买证书的开销也是原因之一。