文章目录
1. URL 和 URI
与 URI(Uniform Resource Identifier,统一资源标识符) 相比,我们更熟悉 URL(UniformResource Locator,统一资源定位符)。URL正是使用 Web 浏览器等访问 Web 页面时需要输入的网页地址。
每个URL地址由两部分组成:存放对象的服务器主机名和对象的路径名。
1.1 统一资源标识符
URI 是 Uniform Resource Identifier 的缩写。RFC2396 分别对这 3 个单词进行了如下定义。
- Uniform:规定统一的格式可方便处理多种不同类型的资源,而不用根据上下文环境来识别资源指定的访问方式。另外,加入新增的协议方案(如http: 或 ftp:)也更容易。
- Resource:资源的定义是“可标识的任何东西”。另外,资源不仅可以是单一的,也可以是多数的集合体。
- Identifier:表示可标识的对象。也称为标识符。
URI 可以用字符串标识某一互联网资源,而 URL表示资源的地点(互联网上所处的位置)。可见 URL是 URI 的子集。
1.2 URL格式
- 登录信息(认证):指定用户名和密码作为从服务器端获取资源时必要的登录信息(身份认证)。此项是可选项。
- 服务器地址:使用绝对 URI 必须指定待访问的服务器地址。地址可以是类似hackr.jp 这种 DNS 可解析的名称,或是 192.168.1.1 这类 IPv4 地址名,还可以是 [0:0:0:0:0:0:0:1] 这样用方括号括起来的 IPv6 地址名。
- 服务器端口号:指定服务器连接的网络端口号。此项也是可选项,若用户省略则自动使用默认端口号。
- 带层次的文件路径:指定服务器上的文件路径来定位特指的资源。这与 UNIX 系统的文件目录结构相似。
- 查询字符串:针对已指定的文件路径内的资源,可以使用查询字符串传入任意参数。此项可选。
- 片段标识符:使用片段标识符通常可标记出已获取资源中的子资源(文档内的某个位置)。但在 RFC 中并没有明确规定其使用方法。该项也为可选项。
2. HTTP报文
HTTP 报文是在 HTTP 应用程序之间发送的数据块。这些数据块以一些文本形式的元信息(meta-information)开头,这些信息描述了报文的内容及含义,后面跟着可选的数据部分。这些报文在客户端、服务器和代理之间流动。
2.1 报文的组成部分
HTTP 报文是简单的格式化数据块。每条报文都包含一条来自客户端的请求,或者一条来自服务器的响应。它们由三个部分组成:对报文进行描述的起始行(start line)、包含属性的首部(header)块,以及可选的、包含数据的主体(body)部分。
2.1.1 起始行
所有的 HTTP 报文都以一个起始行作为开始。
请求行
- 请求报文请求服务器对资源进行一些操作。请求报文的起始行,或称为请求行,包含了一个方法和一个请求 URL,还包含 HTTP 的版本号。
响应行
- 响应报文承载了状态信息和操作产生的所有结果数据,将其返回给客户端。响应报文的起始行,或称为响应行。
- 包含了响应报文使用的 HTTP 版本、数字状态码,以及描述操作状态的文本形式的原因短语。
2.1.2 首部
跟在起始行后面的就是零个、一个或多个 HTTP 首部字段,HTTP 首部字段向请求和响应报文中添加了一些附加信息。本质上来说,它们只是一些名 / 值对(键值对)的列表。
HTTP 规范定义了几种首部字段。应用程序也可以随意发明自己所用的首部。HTTP首部可以分为以下五类。
-
通用首部
:既可以出现在请求报文中,也可以出现在响应报文中。 不论报文是何类型,都为其提供一些有用信息。 -
请求首部
:提供更多有关请求的信息。 -
响应首部
:提供更多有关响应的信息。 -
实体首部
:描述主体的长度和内容,或者资源自身。 -
扩展首部
:规范中没有定义的新首部。
2.1.3 实体的主体部分
HTTP 报文的第三部分是可选的实体主体部分,她与首部之间有一个空行。实体的主体是 HTTP 报文的负荷。就是 HTTP 要传输的内容。
HTTP 报文可以承载很多类型的数字数据:图片、视频、HTML 文档、软件应用程序、信用卡事务、电子邮件等。
2.2 HTTP 方法
GET
:获取资源- GET 方法用来请求访问已被 URI 识别的资源。指定的资源经服务器端解析后返回响应内容。
POST
:传输实体主体- POST 方法用来传输实体的主体。数据就放在报⽂的 body ⾥。
PUT
:传输文件- PUT 方法用来传输文件。就像 FTP 协议的文件上传一样,要求在请求报文的主体中包含文件内容,然后保存到请求 URI 指定的位置。
- 但是,鉴于 HTTP/1.1 的 PUT 方法自身不带验证机制,任何人都可以上传文件 , 存在安全性问题,因此一般的 Web 网站不使用该方法。或者要进行密码验证之后可以上传。
HEAD
:获得报文首部- HEAD 方法和 GET 方法一样,只是不返回报文主体部分。用于确认URI 的有效性及资源更新的日期时间等。
DELETE
:删除文件- DELETE 方法用来删除文件,是与 PUT 相反的方法。DELETE 方法按请求 URI 删除指定的资源。
- 和PUT一样不带验证机制所以存在安全问题。
OPTIONS
:询问支持的方法- OPTIONS 方法用来查询针对请求 URI 指定的资源支持的方法。就是查询某个资源可以使用什么方法,返回GET、POST等。
TRACE
:追踪路径- TRACE 方法是让 Web 服务器端将之前的请求通信环回给客户端的方法。
CONNECT
:要求用隧道协议连接代理- CONNECT 方法要求在与代理服务器通信时建立隧道,实现用隧道协议进行 TCP 通信。主要使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输。
GET 和 POST 的区别是什么?
- GET 是幂等的,POST 不是,也就是GET多次请求和一次请求相同,不会对服务器产生负面影响,而 POST 可能造成表单的重复提交等。
- GET 的 URL 地址可以被缓存到书签,而 POST 不能。
- GET请求会被浏览器主动cache,而POST不会,除非手动设置。
- GET请求只能进行url编码,而POST支持多种编码方式。
- GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留。
- GET请求在URL中传送的参数是有长度限制的,而POST么有。
- 对参数的数据类型,GET只接受ASCII字符,而POST没有限制。
- GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息。
- GET参数通过URL传递,POST放在Request body中。
在 HTTP 协议层面
:- GET 只会发送一个TCP数据包,将首部和实体数据一起发送,然后服务器进行响应。
- 而 POST 会先将数据包的首部发送,服务器响应 100 continue,然后继续发送实体部分,服务器进行处理。也就是总共会发送两个 TCP 数据包。
2.3 HTTP 状态码
状态码的职责是当客户端向服务器端发送请求时,描述返回的请求结果。借助状态码,用户可以知道服务器端是正常处理了请求,还是出现了错误。
这里仅介绍几种常见的状态码
2XX 成功
:2XX 的响应结果表明请求被正常处理了。- 200 OK:表示从客户端发来的请求在服务器端被正常处理了。
- 204 No Content:该状态码代表服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分。
- 206 Partial Content:该状态码表示客户端进行了范围请求(只要其中一部分),而服务器成功执行了这部分的GET 请求。
3XX 重定向
:3XX 响应结果表明浏览器需要执行某些特殊的处理以正确处理请求。- 301 Moved Permanently:永久性重定向。该状态码表示请求的资源已被分配了新的 URI,以后应使用资源现在所指的 URI。
- 302 Found:临时性重定向。该状态码表示请求的资源已被分配了新的 URI,希望用户(本次)能使用新的 URI 访问。
- 303 See Other:该状态码表示由于请求对应的资源存在着另一个 URI,应使用 GET方法定向获取请求的资源。
- 304 Not Modified:该状态码表示客户端发送附带条件的请求时,服务器端允许请求访问资源,但不存在满足条件的情况。
- 307 Temporary Redirect:临时重定向。该状态码与 302 Found 有着相同的含义。
4XX 客户端错误
:4XX 的响应结果表明客户端是发生错误的原因所在。- 400 Bad Request:该状态码表示请求报文中存在语法错误。当错误发生时,需修改请求的内容后再次发送请求。
- 401 Unauthorized:该状态码表示发送的请求需要有通过 HTTP 认证(BASIC 认证、DIGEST 认证)的认证信息。
- 403 Forbidden:该状态码表明对请求资源的访问被服务器拒绝了。
- 404 Not Found:该状态码表明服务器上无法找到请求的资源。
5XX 服务器错误
:5XX 的响应结果表明服务器本身发生错误。- 500 Internal Server Error:该状态码表明服务器端在执行请求时发生了错误。
- 502 Bad Gateway:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。连接上了响应超时。
- 503 Service Unavailable:该状态码表明服务器暂时处于超负载或正在进行停机维护,现在无法处理请求。
- 504 Gateway Time-out:作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。 得不到响应。
3. HTTP 连接
HTTP 连接的性能很大程度上取决于TCP连接的性能,此处暂不考虑TCP的种种具体实现,只要知道TCP的连接和断开连接都存在时延。如果只对连接进行简单的管理,TCP 的性能时延可能会叠加起来。
例如, 假设有一个包含了 3 个嵌入图片的 Web 页面。浏览器需要发起 4 个 HTTP 事务来显示此页面:1 个用于顶层的 HTML 页面,3 个用于嵌入的图片。如果每个事务都需要(串行地建立)一条新的连接,那么连接时延和慢启动时延就会叠加起来。
所以提供了一些方法可以提高HTTP的连接性能:
- 并行连接:通过多条 TCP 连接发起并发的 HTTP 请求。
- 持久连接:重用 TCP 连接,以消除连接及关闭时延。
- 管道化连接:通过共享的 TCP 连接发起并发的 HTTP 请求。
- 复用的连接:交替传送请求和响应报文(实验阶段)。
以下会进行详细介绍:
3.1 并行连接
HTTP 允许客户端打开多条连接,并行地执行多个 HTTP 事务。在这个例子中,并行加载了四幅嵌入式图片,每个事务都有自己的 TCP 连接。
并行连接可能会提高页面的加载速度
- 包含嵌入对象的组合页面如果能(通过并行连接)克服单条连接的空载时间和带宽限制,加载速度也会有所提高。
- 时延可以重叠起来,而且如果单条连接没有充分利用客户端的因特网带宽,可以将未用带宽分配来装载其他对象。
并行连接不一定更快
- 客户端的网络带宽不足时,大部分的时间可能都是用来传送数据的。
- 打开大量连接会消耗很多内存资源,从而引发自身的性能问题。
实际上,浏览器确实使用了并行连接,但它们会将并行连接的总数限制为一个较小的值(通常是 4 个)。服务器可以随意关闭来自特定客户端的超量连接。
3.2 持久连接
Web 客户端经常会打开到同一个站点的连接。因此,初始化了对某服务器 HTTP 请求的应用程序很可能会在不久的将来对那台服务器发起更多的请求,这种性质被称为站点局部性(site locality)
因此,HTTP/1.1(以及 HTTP/1.0 的各种增强版本)允许 HTTP 设备在事务处理结束之后将 TCP 连接保持在打开状态,以便为未来的 HTTP 请求重用现存的连接。在事务处理结束之后仍然保持在打开状态的 TCP 连接被称为持久连接。非持久连接会在每个事务结束之后关闭。持久连接会在不同事务之间保持打开状态,直到客户端或服务器决定将其关闭为止。
重用已对目标服务器打开的空闲持久连接,就可以避开缓慢的连接建立阶段。而且,已经打开的连接还可以避免慢启动的拥塞适应阶段,以便更快速地进行数据的传输。
并行连接的缺点
- 每个事务都会打开 / 关闭一条新的连接,会耗费时间和带宽。
- 由于 TCP 慢启动特性的存在,每条新连接的性能都会有所降低。
- 可打开的并行连接数量实际上是有限的。
持久连接的缺点
- 如果对于持久连接管理不妥,就会累积出大量的空闲连接,耗费本地以及远程客户端和服务器上的资源。
持久连接与并行连接配合使用可能是最高效的方式。现在,很多 Web 应用程序都会打开少量的并行连接,其中的每一个都是持久连接。
3.2.1 HTTP/1.0+ keep-alive连接
- 实现 HTTP/1.0 keep-alive 连接的客户端可以通过包含 Connection: Keep-Alive 首部请求将一条连接保持在打开状态。
- keep-Alive 首部只是请求将连接保持在活跃状态。发出 keep-alive 请求之后,客户端和服务器并不一定会同意进行 keep-alive 会话。它们可以在任意时刻关闭空闲的 keep-alive 连接,并可随意限制 keep-alive 连接所处理事务的数量。
- 对于那些不理解 Connection 首部,而且不知道在沿着转发链路将其发送出去之前,应该将该首部删除的代理。会出现哑代理的情况。
- Web 客户端向代理发送了一条报文,其中包含了 Connection:Keep-Alive 首部,如果可能的话请求建立一条 keep-alive 连接。客户端等待响应,以确定对方是否认可它对 keep-alive 信道的请求。
- 哑代理收到了这条 HTTP 请求,但它并不理解 Connection 首部(只是将其作为一个扩展首部对待)因此只是沿着转发链路将报文一字不漏地发送给服务器,但 Connection 首部是个逐跳首部,只适用于单条传输链路,不应该沿着传输链路向下传输。
- Web 服务器收到经过代理转发的 Connection: Keep-Alive 首部时,会误以为代理希望进行 keep-alive 对话。
- 同样客户端也会接收到代理直接响应回来的Keep-Alive
- 所以客户端和服务器都会和代理建立起keepAlive连接,但是代理并不知道这是什么东西,只会在每次连接结束之后关闭连接。
- 为避免此类代理通信问题的发生,现代的代理都绝不能转发 Connection 首部和所有名字出现在 Connection 值中的首部。
- 为解决该问题,又引入了一个名为Proxy-Connection 的新首部。
- 浏览器会向代理发送非标准的 Proxy-Connection 扩展首部,而不是官方支持的著名的 Connection 首部。如果代理是盲中继,它会将无意义的 Proxy-Connection 首部转发给 Web 服务器,服务器会忽略此首部。
- 如果代理是个聪明的代理(能够理解持久连接的握手动作),就用一个 Connection 首部取代无意义的 Proxy-Connection 首部,然后将其发送给服务器。
3.2.2 HTTP/1.1持久连接
HTTP/1.1 逐渐停止了对 keep-alive 连接的支持,用一种名为持久连接(persistentconnection)的改进型设计取代了它。
与 HTTP/1.0+ 的 keep-alive 连接不同,HTTP/1.1 持久连接在默认情况下是激活的。如果要在事务处理结束之后将连接关闭,HTTP/1.1 应用程序必须向报文中显式地添加一个 Connection:close 首部。
3.3 管道化连接
HTTP/1.1 允许在持久连接上可选地使用请求管道。这是相对于 keep-alive 连接的又一性能优化。
在响应到达之前,可以将多条请求放入队列。当第一条请求通过网络流向地球另一端的服务器时,第二条和第三条请求也可以开始发送了。在高时延网络条件下,这样做可以降低网络的环回时间,提高性能。
对管道化连接有几条限制
- 如果 HTTP 客户端无法确认连接是持久的,就不应该使用管道。
- 必须按照与请求相同的顺序回送 HTTP 响应。
- HTTP 客户端必须做好连接会在任意时刻关闭的准备,还要准备好重发所有未完成的管道化请求。
- HTTP 客户端不应该用管道化的方式发送会产生副作用的请求(例如POST请求,无法安全的重试,可能造成表单重复提交等)。
3.4 关闭连接的奥秘
所有 HTTP 客户端、服务器或代理都可以并且可能在任意时刻关闭一条 TCP 传输连接。
每条 HTTP 响应都应该有精确的 Content-Length 首部,用以描述响应主体的尺寸。客户端或代理收到一条随连接关闭而结束的 HTTP 响应之后就应该根据 Content-Length 和接收到的主题校验数据的正确性。
即使在非错误情况下,连接也可以在任意时刻关闭。HTTP 应用程序要做好正确处理非预期关闭的准备。如果在客户端执行事务的过程中,传输连接关闭了,那么,除非事务处理会带来一些副作用,否则客户端就应该重新打开连接,并重试一次。
有些事务,比如 GET 一个静态的 HTML 页面,可以反复执行多次,也不会有什么变化。而其他一些事务,比如向一个在线书店 POST 一张订单,就不能重复执行,不然会有下多张订单的危险。
如果一个事务,不管是执行一次还是很多次,得到的结果都相同,这个事务就是幂等的。
4. 与 HTTP 协作的 Web 服务器
4.1 代理
代理是一种有转发功能的应用程序,它扮演了位于服务器和客户端“中间人”的角色,接收由客户端发送的请求并转发给服务器,同时也接收服务器返回的响应并转发给客户端。
使用代理服务器的理由:
- 利用缓存技术减少网络带宽的流量。
- 组织内部针对特定网站的访问控制,以获取访问日志为主要目的。
缓存代理:代理转发响应时,缓存代理(Caching Proxy)会预先将资源的副本(缓存)保存在代理服务器上。当代理再次接收到对相同资源的请求时,就可以不从源服务器那里获取资源,而是将之前缓存的资源作为响应返回。
透明代理:转发请求或响应时,不对报文做任何加工的代理类型被称为透明代理(Transparent Proxy)。反之,对报文内容进行加工的代理被称为非透明代理。
4.2 网关
网关的工作机制和代理十分相似。而网关能使通信线路上的服务器提供非 HTTP 协议服务。网关类似提供了一个翻译器的功能。
网关是资源和应用程序之间的粘合剂。应用程序可以(通过 HTTP 或其他已定义的接口)请求网关来处理某条请求,网关可以提供一条响应。网关可以向数据库发送查询语句,或者生成动态的内容,就像一个门一样:进去一条请求,出来一个响应。
利用网关能提高通信的安全性,因为可以在客户端与网关之间的通信线路上加密以确保连接的安全。然后由网关解密之后向服务器发送HTTP请求。
4.3 隧道
Web 隧道可以通过 HTTP 应用程序访问使用非 HTTP 协议的应用程序。
Web 隧道允许用户通过 HTTP 连接发送非 HTTP 流量,这样就可以在 HTTP 上捎带其他协议数据了,使用 Web 隧道最常见的原因就是要在 HTTP 连接中嵌入非 HTTP流量,这样,这类流量就可以穿过只允许 Web 流量通过的防火墙了。
最初开发 Web 隧道是为了通过防火墙来传输加密的 SSL 流量。很多组织都会将所有流量通过分组过滤路由器和代理服务器以隧道方式传输,以提升安全性。但有些协议,比如加密 SSL,其信息是加密的,无法通过传统的代理服务器转发。隧道会通过一条 HTTP 连接来传输 SSL 流量,以穿过端口 80 的 HTTP 防火墙。
简单来说就是,隧道可按要求建立起一条与其他服务器的通信线路,届时使用 SSL等加密手段进行通信。隧道的目的是确保客户端能与服务器进行安全的通信。
4.4 缓存
缓存是指代理服务器或客户端本地磁盘内保存的资源副本。利用缓存可减少对源服务器的访问,因此也就节省了通信流量和通信时间。
缓存不仅可以存在于缓存服务器内,还可以存在客户端浏览器中。浏览器缓存如果有效,就不必再向服务器请求相同的资源了,可以直接从本地磁盘内读取。
另外,当判定缓存过期后,会向源服务器确认资源的有效性。若判断浏览器缓存失效,浏览器会再次请求新资源。
5. 识别、认证与安全
5.1 识别
HTTP 最初是一个匿名、无状态的请求 / 响应协议。服务器处理来自客户端的请求,然后向客户端回送一条响应。Web 服务器几乎没有什么信息可以用来判定是哪个用户发送的请求,也无法记录来访用户的请求序列。
产生如下几种用户识别机制
- 利用HTTP 请求首部来承载用户相关信息,例如下图
- 利用客户端 IP 地址作为一种标识形式。
- 通过用户名和密码进行认证(登录)来显式地询问用户是谁。登录完成之后,用 www-Authenticate 首部和 Authorization 首部向 Web 站点传送用户的相关信息。
- 有些 Web 站点会为每个用户生成特定版本的 URL 来追踪用户的身份。改动后包含了用户状态信息的 URL 被称为胖 URL(fat URL)。
- 利用cookie,以下详谈。
5.1.1 cookie
cookie 是当前识别用户,实现持久会话的最好方式。Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。
可以笼统地将 cookie 分为两类:
会话 cookie
: 是一种临时 cookie,它记录了用户访问站点时的设置和偏好。用户退出浏览器时,会话cookie 就被删除了。持久 cookie
: 生存时间更长一些;它们存储在硬盘上,浏览器退出,计算机重启时它们仍然存在。通常会用持久 cookie 维护某个用户会周期性访问的站点的配置文件或登录名。
Cookie 会根据从服务器端发送的响应报文内的一个叫做 Set-Cookie 的首部字段信息,通知客户端保存 Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入 Cookie 值后发送出去。
服务器端发现客户端发送过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
cookie 和 session 的区别
- cookie数据存放在客户的浏览器上,session数据放在服务器上。
- cookie不是很安全,别人可以分析存放在本地的cookie并进行cookie欺骗考虑到安全应当使用session。
- session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能,考虑到减轻服务器性能方面,应当使用cookie。
- 单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie,session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型。
5.1.2 token
由于 cookie 不安全且保存数据有限制,session 占用服务器端内存,并且不适用于移动端,所以引入 token 机制。例如用户登录,只需要第一次登录的时候进行验证,登录成功之后生成一个 token 返回给客户端,客户端保存token 并且每次请求将token 放置在 HTTP 首部的Authorization字段即可。
JWT(Json Web Token)就是一种具体的实现机制。
JWT 本质上就是⼀段签名的 JSON 格式的数据。由于它是带有签名的,因此接收者便可以验证它的真实性。
JWT 主要包括三部分
Header(首部)
:描述 JWT 的元数据。定义了生成签名的算法以及 Token 的类型。Payload(负载)
:用来存放实际需要传递的数据。Signature(签名)
:服务器通过 Payload 、 Header 和⼀个密钥( secret ) 使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。
5.2 认证
认证就是要给出一些身份证明。
HTTP 定义了两个官方的认证协议:基本认证和摘要认证。今后人们可以随意设计一些使用 HTTP 质询 / 响应框架的新协议。
HTTP 提供了一个原生的质询 / 响应(challenge/response)框架,Web 应用程序收到一条 HTTP 请求报文时,服务器没有按照请求执行动作,而是以一个“认证质询”进行响应,要求用户提供一些保密信息来说明他是谁,从而对其进行质询。
- 服务器对用户进行质询时,会返回一条 401 Unauthorized 响应,并在 WWW-Authenticate首部说明如何以及在哪里进行认证。
- 当客户端授权服务器继续处理时,会重新发送请求,但会在 Authorization 首部附上加密的密码和其他一些认证参数。
- 授权请求成功完成时,服务器会返回一个正常的状态码(比如,200 OK);对高级认证算法来说,可能还会在 Authentication-Info 首部附加一些额外的信息(。
-
基本认证
- 基本认证的过程
- 在基本认证中,Web 服务器可以拒绝一个事务,质询客户端,请用户提供有效的用户名和密码。
- 服务器会返回 401 状态码,而不是 200 状态码来初始化认证质询,并用 WWW-Authenticate 响应首部指定要访问的安全域。
- 浏览器收到质询时,会打开一个对话框,请求用户输入这个域的用户名和密码。然后将用户名和密码稍加扰码,再用 Authorization 请求首部回送给服务器。
- 中间的代理服务器也可以实现认证功能。通过代理服务器提供对某组织内部资源的统一访问控制是一种很便捷的方式。代理认证的步骤与 Web 服务器身份验证的步骤相同。但首部和状态码都有所不同。
- 基本认证简单便捷,但并不安全(例如,发送账户密码很容易解码得到)。只能用它来防止非恶意用户无意间进行的访问,或将其与 SSL 这样的加密技术配合使用。
- 基本认证的过程
-
摘要认证
- 摘要认证主要是对基本认证的改进,但还没被广泛使用。
- 客户端不会发送密码,而是会发送一个“指纹”或密码的“摘要”,这是密码的不可逆扰码。
- 用随机数防止截获摘要,并一遍遍地重放给服务器。
- 摘要认证的握手机制
6. HTTPS
使用 HTTPS 时,所有的 HTTP 请求和响应数据在发送到网络之前,都要进行加密。HTTPS 在 HTTP 下面提供了一个传输级的密码安全层——可以使用SSL,也可以使用其后继者——传输层安全(Transport Layer Security,TLS)。
在对HTTPS进行讨论之前,先介绍几个术语:
密码
:对文本进行编码,使偷窥者无法识别的算法。密钥
:改变密码行为的数字化参数。对称密钥加密系统
:编 / 解码使用相同密钥的算法。- 每次发送密文的同时会把密钥也一起发送,给对方解密。
不对称密钥加密系统
:编 / 解码使用不同密钥的算法。- 一把叫做私有密钥(private key),另一把叫做公开密钥(public key)。顾名思义,私有密钥不能让其他任何人知道,而公开密钥则可以随意发布,任何人都可以获得。
- 发送密文的一方使用对方的公开密钥进行加密处理,对方收到被加密的信息后,再使用自己的私有密钥进行解密。这样就不需要将密钥传输给对方
公开密钥加密系统
:一种能够使数百万计算机便捷地发送机密报文的系统。数字签名
:用来验证报文未被伪造或篡改的校验和。数字证书
:由一个可信的组织验证和签发的识别信息。- 可以对公有密钥颁发证书,保证公有密钥是目标服务器的密钥。
6.1 RSA 算法的 TLS 握手
传统的 TLS 握⼿基本都是使⽤ RSA 算法来实现密钥交换的,在将 TLS 证书部署服务端时,证书⽂件中包含⼀对公私钥,其中公钥会在 TLS 握⼿阶段传递给客户端,私钥则⼀直留在服务端,⼀定要确保私钥不能被窃取。
在 RSA 密钥协商算法中,客户端会⽣成随机密钥,并使⽤服务端的公钥加密后再传给服务端。根据⾮对称加密算法,公钥加密的消息仅能通过私钥解密,这样服务端解密后,双⽅就得到了相同的密钥,再用它加密应⽤消息。
第一次握手
客户端⾸先会发⼀个「Client Hello」消息,消息⾥⾯有客户端使⽤的 TLS 版本号、⽀持的密码套件列表,以及⽣成的客户端随机数(Client Random),这个随机数会被服务端保留,它是⽣成对称加密密钥的材料之⼀。
第⼆次握⼿
当服务端收到客户端的「Client Hello」消息后,会确认 TLS 版本号是否⽀持,和从密码套件列表中选择⼀个密码套件,以及⽣成服务端随机数(Server Random)。
接着,返回「Server Hello」消息,消息⾥⾯有服务器确认的 TLS 版本号,也给出了随机数(Server Random),和选择的密码套件。
然后,服务端为了证明⾃⼰的身份,会发送「Server Certificate」给客户端,这个消息⾥含有数字证书。
数字证书是什么?
- ⼀个数字证书通常包含了:
- 公钥;
- 持有者信息;
- 证书认证机构(CA)的信息;
- CA 对这份⽂件的数字签名及使⽤的算法;
- 证书有效期;
- 还有⼀些其他额外信息
- 数字证书签发流程
- ⾸先 CA 会把持有者的公钥、⽤途、颁发者、有效时间等信息打成⼀个包,然后对这些信息进⾏ Hash 计算,得到⼀个 Hash 值;
- 然后 CA 会使⽤⾃⼰的私钥将该 Hash 值加密,⽣成 Certificate Signature,也就是 CA 对证书做了签名;
- 最后将 Certificate Signature 添加在⽂件证书上,形成数字证书;
- 数字证书验证过程
- ⾸先客户端会使⽤同样的 Hash 算法获取该证书的 Hash 值 H1;
- 通常浏览器和操作系统中集成了 CA 的公钥信息,浏览器收到证书后可以使⽤ CA 的公钥解密 CertificateSignature 内容,得到⼀个 Hash 值 H2 ;
- 最后⽐较 H1 和 H2,如果值相同,则为可信赖的证书,否则则认为证书不可信。
第三次握手
客户端验证完证书后,认为可信则继续往下⾛。接着,客户端就会⽣成⼀个新的随机数 (pre-master),⽤服务器的 RSA 公钥加密该随机数,通过「Change Cipher Key Exchange」消息传给服务端。
服务端收到后,⽤ RSA 私钥解密,得到客户端发来的随机数 (pre-master)。
客户端和服务端双⽅都共享了三个随机数,分别是 Client Random、Server Random、pre-master。于是,双⽅根据已经得到的三个随机数,⽣成会话密钥(Master Secret),也就是对称密钥,⽤于对后续的 HTTP请求/响应的数据加解密。
⽣成完会话密钥后,然后客户端发⼀个「Change Cipher Spec」,告诉服务端开始使⽤加密⽅式发送消息。
然后,客户端再发⼀个「Encrypted Handshake Message(Finishd)」消息,把之前所有发送的数据做个摘要,再⽤会话密钥(master secret)加密⼀下,让服务器做个验证,验证加密通信是否可⽤和之前握⼿信息是否有被中途篡改过。
在发送「Change Cipher Spec」之前传输的 TLS 握⼿数据都是明⽂,之后都是对称密钥加密的密⽂。
第四次握⼿
服务器也是同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双⽅都验证加密和解密没问题,那么握⼿正式完成。
最后,就⽤「会话密钥」加解密 HTTP 请求和响应了。
RSA 算法的缺陷
使⽤ RSA 密钥协商算法的最⼤问题是不⽀持前向保密。因为客户端传递随机数(⽤于⽣成对称加密密钥的条件之⼀)给服务端时使⽤的是公钥加密的,服务端收到到后,会⽤私钥解密得到随机数。所以⼀旦服务端的私钥泄漏了,过去被第三⽅截获的所有 TLS 通讯密⽂都会被破解。
6.2 ECDHE 算法的 TLS 握手过程
第一次握手
客户端⾸先会发⼀个「Client Hello」消息,消息⾥⾯有客户端使⽤的 TLS 版本号、⽀持的密码套件列表,以及⽣成的随机数(Client Random)。这和上面利用RSA算法的过程相同。
第⼆次握⼿
服务端收到客户端的消息之后会返回「Server Hello」消息,消息⾯有服务器确认的 TLS 版本号,也给出了⼀个随机数(Server Random),然后从客户端的密码套件列表选择了⼀个合适的密码套件。
接着,服务端为了证明⾃⼰的身份,发送「Certificate」消息,会把证书也发给客户端。
因为服务端选择了 ECDHE 密钥协商算法,所以会在发送完证书后,发送「Server Key Exchange」消息。
这个过程服务器做了三件事:
- 选择了椭圆曲线,选好了椭圆曲线相当于椭圆曲线基点也定好了,这些都会公开给客户端;
- ⽣成随机数作为服务端椭圆曲线的私钥,保留到本地;
- 根据基点和私钥计算出服务端的椭圆曲线公钥,这个会公开给客户端。
为了保证这个椭圆曲线的公钥不被第三⽅篡改,服务端会⽤ RSA 签名算法给服务端的椭圆曲线公钥做个签名。
随后,就是「Server Hello Done」消息,⾄此,TLS 两次握⼿就已经完成了,⽬前客户端和服务端通过明⽂共享了这⼏个信息:Client Random、Server Random 、使⽤的椭圆曲线、椭圆曲线基点、服务端椭圆曲线的公钥。
第三次握手
客户端收到了服务端的证书后,⾃然要校验证书是否合法,确认证书的真实性,再⽤证书的公钥验证签名,这样就能确认服务端的身份。
之后,客户端会⽣成⼀个随机数作为客户端椭圆曲线的私钥,然后再根据服务端前⾯给的信息,⽣成客户端的椭圆曲线公钥,然后⽤「Client Key Exchange」消息发给服务端。
至此,所有的信息都已经共享,最终的会话密钥,就是⽤「客户端随机数 + 服务端随机数 + x(ECDHE 算法算出的共享密钥,通过椭圆曲线,椭圆基点,对方公钥和自己的私钥计算x坐标得到) 」三个材料⽣成的。
算好会话密钥后,客户端会发⼀个「Change Cipher Spec」消息,告诉服务端后续改⽤对称算法加密通信。
接着,客户端会发「Encrypted Handshake Message」消息,把之前发送的数据做⼀个摘要,再⽤对称密钥加密⼀下,让服务端做个验证,验证下本次⽣成的对称密钥是否可以正常使⽤。
第四次握⼿
最后,服务端也会有⼀个同样的操作,发「Change Cipher Spec」和「Encrypted Handshake Message」消息,如果双⽅都验证加密和解密没问题,那么握⼿正式完成。于是,就可以正常收发加密的 HTTP 请求和响应了。
6.3 HTTP 与 HTTPS 有哪些区别?
- HTTP 是超⽂本传输协议,信息是明⽂传输,存在安全⻛险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在TCP 和 HTTP ⽹络层之间加⼊了 SSL/TLS 安全协议,使得报⽂能够加密传输。
- HTTP 连接建⽴相对简单, TCP 三次握⼿之后便可进⾏ HTTP 的报⽂传输。⽽ HTTPS 在 TCP 三次握⼿之后,还需进⾏ SSL/TLS 的握⼿过程,才可进⼊加密报⽂传输。
- HTTP 的端⼝号是 80,HTTPS 的端⼝号是 443。
- HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。
7. HTTP/1.1、HTTP/2、HTTP/3 演变
HTTP/1.1 相⽐ HTTP/1.0 性能上的改进:
- 使⽤ TCP ⻓连接的⽅式改善了 HTTP/1.0 短连接造成的性能开销。
- ⽀持管道化⽹络传输,只要第⼀个请求发出去了,不必等其回来,就可以发第⼆个请求出去,可以减少整体的响应时间。
- 支持断点续传,也就是通过标记来进行分块传输。例如一次文件上传中途断网失败,下次可以从切断位置继续上传,通过指定Content-Range字段来表示从文件流的什么位置开始读取。
HTTP/1.1 的缺陷
- 请求 / 响应头部(Header)未经压缩就发送,⾸部信息越多延迟越⼤。只能压缩 Body 的部分;
- 发送冗⻓的⾸部。每次互相发送相同的⾸部造成的浪费较多;
- 服务器是按请求的顺序响应的,如果服务器响应慢,会招致客户端⼀直请求不到数据,也就是队头阻塞,没有请求优先级控制;
- 请求只能从客户端开始,服务器只能被动响应。
HTTP/1.1 的优化
- 使用缓存,减少发送HTTP请求。
- 减少重定向次数,将重定向交给代理。
- 合并请求,将一些小请求合并成一个大请求。
- 延迟放松请求,对于一些暂时用不到的请求延迟发送。
- 压缩响应的资源
HTTP/2 相⽐ HTTP/1.1 性能上的改进:
-
头部压缩
- HTTP/2 会压缩头(Header)如果你同时发出多个请求,他们的头是⼀样的或是相似的,那么,协议会帮你消除重复的部分。
- 这就是所谓的 HPACK 算法:在客户端和服务器同时维护⼀张头信息表,所有字段都会存⼊这个表,⽣成⼀个索引号,以后就不发送同样字段了,只发送索引号,这样就提⾼速度了。
-
⼆进制格式
- HTTP/2 不再像 HTTP/1.1 ⾥的纯⽂本形式的报⽂,⽽是全⾯采⽤了⼆进制格式,头信息和数据体都是⼆进制,并且统称为帧(frame):头信息帧和数据帧。增加了数据传输的效率。
-
数据流
- HTTP/2 的数据包不是按顺序发送的,同⼀个连接⾥⾯连续的数据包,可能属于不同的回应。因此,必须要对数据包做标记,指出它属于哪个回应。
- 客户端还可以指定数据流的优先级。优先级⾼的请求,服务器就先响应该请求。
-
多路复⽤
- HTTP/2 是可以在⼀个连接中并发多个请求或回应,⽽不⽤按照顺序⼀⼀对应。
- 降低了延迟,⼤幅度提⾼了连接的利⽤率。
-
服务器推送
- HTTP/2 还在⼀定程度上改善了传统的「请求 - 应答」⼯作模式,服务不再是被动地响应,也可以主动向客户端发送消息。
HTTP/2 的缺陷
- 多个 HTTP 请求在复⽤⼀个 TCP 连接,下层的 TCP 协议是不知道有多少个 HTTP 请求的。所以⼀旦发⽣了丢包现象,就会触发 TCP 的重传机制,这样在⼀个 TCP 连接中的所有的 HTTP 请求都必须等待这个丢了的包被重传回来。
HTTP / 3 的改进
- 由于是基于 TCP 传输层的问题,所以 HTTP/3 把 HTTP 下层的 TCP 协议改成了 UDP!
- 基于 UDP 的 QUIC 协议 可以实现类似 TCP 的可靠性传输。
参考文献
《图解HTTP》
《HTTP权威指南》
《计算机网络-自顶向下方法》
https://mp.weixin.qq.com/s/bUy220-ect00N4gnO0697A