前言
《图解 HTTP》这本书把我多年的散装网络给治好了,不像《计算机网络》那么枯燥,读起来非常的畅快,这篇文章算是一份笔记,记录了我觉得 ioser(不是 loser...)应该掌握的关于网络的知识点,在末尾也做了一些补充(因为书里没有讲的那么细,也是比较常见的面试题)。
看完这篇文章,你可以了解以下知识:
- HTTP 协议是什么,作用
- TCP、IP、DNS 的概念
- URI 和 URL 的区别
- Cookie 的作用
- 代理、网关、隧道的概念
- HTTP 的缺点
- HTTPS 的作用、原理
- HTTPS 的通信机制
- SSL、TLS 的概念
- ...
在文章的最后,补充了关于以下知识点:
- TCP 的三次握手和四次挥手的过程及原因
- TCP 和 UDP 的差异
- SSL 和 TLS 的关系
- Charles 为什么可以抓到 HTTPS 的包
万字长文,字数有点多,但是读起来很轻松(dog head),让我们开始。
一、了解 Web 及网络基础
平时我们在浏览器中输入 URL 后,可以看到 Web 页面,这些 Web 页面并不是凭空出来的,是 Web 浏览器根据地址栏中指定的 URL,从 Web 服务器端获取文件资源(resource)等信息,从而显示出 Web 页面。
我们将 通过发送请求获取服务器资源的 Web 浏览器等,称为 客户端。
客户端与服务器端之间的通信,采用了一种叫做 HTTP(HyperText Transfer Protocol,超文本传输协议) 的协议作为规范。协议就是规则的约定,可以说,Web 是建立在 HTTP 协议上通信的。
TCP/IP
为了理解 HTTP,我们需要先了解一下 TCP/IP 协议族。
通常使用的网络(包括互联网)是在 TCP/IP 协议族的基础上运作的,而 HTTP 属于它内部的一个子集。
计算机与网络设备要相互通信,双方就必须基于相同的方法。比如,如何探测到通信目标、由哪一边先发起通信、使用哪种语言进行通信、怎样结束通信等规则都得实现确定,不同的硬件、操作系统之间的通信,都需要一种规则,而我们就把这种规则称为协议(protocol)。
TCP/IP 是互联网相关的各类协议族的总称。
分层管理
TCP/IP 协议族按层次分成 4 层:
- 应用层
- 传输层
- 网络层
- 数据链路层
应用层决定了向用户提供应用服务时通信的活动,HTTP 协议也处于该层。
传输层对上层应用层,提供处于网络连接中的两台计算机之间的数据传输,在传输层中有 TCP(Transmission Control Protocol,传输控制协议)和 UDP(User Data Protocol,用户数据报协议)。
网络层用来处理在网络上流动的数据包。数据包是网络传输的最小数据单位。
链路层用来处理连接网络的硬件部分。
通信传输流
利用 TCP/IP 协议族进行网络通信时,会通过分层顺序与对方进行通信。发送端从应用层往下走,接收端则从链路层往上走。
发送端在层与层之间传输数据时,每经过一层必定会被打上一个该层所属的首部信息。反之,接收端在层与层传输数据时,每经过一层时会把对应的首部消去。
这些把数据包装起来的做法称为封装。
IP、TCP 和 DNS
IP
IP(Internet Protocol)网际协议 位于网络层,作用是 把各种数据包传送给对方。而要保证确实传送到对方那里,则需要满足各类条件,其中两个重要的条件是 IP地址 和 MAC地址(Media Access Control Address)。
有人会把 "IP" 和 "IP地址" 搞混,"IP" 其实是一种协议的名称。
IP 地址指明了节点被分配到的地址,MAC 地址是指网卡所属的固定地址。IP 地址可以和 MAC 地址进行配对。IP 地址可变换,但 MAC 地址基本上不会更改。
在网络上,通信的双方经常是通过多台计算机和网络设备中转才能连接到对方,而在进行中转时,会利用下一站中转设备的 MAC 地址来搜索下一个中转目标。这时,会采用 ARP 协议(Address Resolution Protoco),是一种用以解析地址的协议,根据通信方的 IP 地址就可以反查出对应的 MAC 地址。
没有人能够全面掌握互联网中的传输状况,在到达通信目标前的中转过程中,那些计算机和路由器等网络设备只能获悉很粗略的传输路线。这种机制称为 路由选择(routing)。
有点像快递公司的送货过程。想要寄快递的人,只要将自己的货物送到集散中心,就可以知道快递公 司是否肯收件发货,该快递公司的集散中心检查货物的送达地址,明 确下站该送往哪个区域的集散中心。接着,那个区域的集散中心自会 判断是否能送到对方的家中。
每个中转设备都只能掌握一部分的传输路线信息。
TCP
TCP 位于传输层,提供可靠的字节流服务。
字节流服务(Byte Stream Service)是指,为了方便传输,将大块数据分割成以报文段(segment)为单位的数据包进行管理。
可靠的传输服务指,能够把数据准确可靠的传给对方。
为了准确无误的将数据送达目标处,TCP 协议采用了 三次握手(three-way handshaking) 策略。
- 发送端首先发送一个带有 SYN 的数据包给对方。
- 接收端收到后,回传一个带有 SYN/ACK 标志的数据包以示传达确认信息。
- 发送端再回传一个带有 ACK 标志的数据包,代表 "握手" 结束。
DNS
DNS(Domain Name System) 提供域名到 IP 地址之间的解析服务。 也是位于应用层的协议。
如: 域名:www.hackr.jp
对应的
IP 地址:20X.189.105.112
通常用户使用主机名或域名来访问对方的计算机,而计算机更擅长的是处理一长串数字,也就是 IP 地址,所以 DNS 服务应运而生,通过提供域名查找 IP 地址,或逆向通过 IP 地址反查域名。
各种协议与 HTTP 协议的关系
URI 和 URL
URL(Uniform Resource Locator,统一资源定位符),就是使用 Web 浏览器等访问 Web 页面时需要输入的 网页地址。
比如
https://www.baidu.com/就是 URL。
URI 是 Uniform Resource Identifier 的缩写,就是由某个协议方案表示的资源的定位标识符。协议方案就是指访问资源所使用的协议类型名称。
采用 HTTP 协议时,协议方案就是 http。
URI 用字符串标识某一互联网资源,而 URL 表示资源的地点(互联网上所处的位置),可见 URL 是 URI 的子集。
二、简单的 HTTP 协议
HTTP 协议用于客户端与服务器端之间的通信。
请求访问 文本或图像等资源的一端称为 客户端,而 提供资源响应 的一端称为 服务器端。
HTTP 协议规定,请求从客户端发出,最后服务器端响应该请求并返回。换句话说,肯定是先从客户端开始建立通信的,服务器端在没有接收到请求之前不会发送响应。
HTTP 是一种不保存状态,即无状态(stateless)协议。
每当有新的请求发送时,就会有对应的新响应产生,协议本身不保留之前一切的请求或响应报文的信息。这是为了更快的处理大量事务,确保协议的可伸缩性,而特意把 HTTP 协议设计成如此简单的。
但是,随着 Web 不断发展,因为无状态而导致业务处理变得棘手的情况增多了,比如保持用户的登录状态等,于是引入了 Cookie 技术,稍后会讲。
HTTP 方法
- GET:用来请求访问已被 URI 识别的资源
- POST:用来传输实体的主体
- PUT:传输文件(有安全性问题,一般不使用)
- HEAD:获得报文首部(与 GET 一样,只是不返回报文主体)
- DELETE:删除文件(与 PUT 相反,不安全,一般不使用)
- OPTIONS:查询针对请求 URI 指定的资源支持的方法(比如返回该资源支持 GET 和 HEAD 方法)
- TRACE:让 Web 服务器将之前的通信环回给客户端的方法,客户端可以查询发出去的请求是怎样被加工/篡改的。(不怎么常用,并且容易引入 XST(Cross-Site Tracing,跨站追踪))
- CONNECT:要求在于代理服务器通信时建立隧道,实现用隧道协议进行 TCP 通信。主要使用 SSL(Secure Sockets Layer,安全套接层)和 TLS(Transport Layer Security,传输层安全)协议把通信内容加密后经网络隧道传输
持久连接节省通信量
在 HTTP 协议的初始版本中,每进行一次 HTTP 通信就要断开一次 TCP 连接。
随着 HTTP 的普及,现在一个 HTML 页面可能包含很多个资源。比如,在使用浏览器浏览一个包含多张图片的 HTML 页面时,在发送请求访问 HTML 页面资源的同时,也会请求该 HTML 页面里包含的其他资源。因此,每次的请求都会造成无谓的 TCP 连接和断开,增加通信量的开销。
为了解决这个问题,提出了 持久连接(HTTP Persistent Connections, 也称为 HTTP keep-alive 或 HTTP connection reuse) 方法,特点是,只要任意一端没有明确提出断开连接,则保持 TCP 连接状态。
持久连接使得多数请求以管线化(pipelining)方式发送成为可能。从前发出请求后需等待并收到响应,才能发送下一个请求。管线化技术可以实现 不同等待响应也可以直接发送下一个请求。
使用 Cookie 的状态管理
HTTP 是无状态协议,无状态协议的优点是可以减少服务器的 CPU 及内存资源的消耗,缺点是有些业务场景没有状态管理的话处理起来很繁琐。
比如要求登录认证的 Web 页面,如果不进行状态管理,那每次跳转新页面不是要再次登录,就是要在每次的请求报文中附加参数来管理登录状态。
为了保留无状态协议的优点,又解决状态管理的问题,所以引入了 Cookie 技术。Cookie 技术通过在请求和响应报文中写入 Cookie 信息来控制客户端的状态。
Cookie 会根据从服务器端发送的响应报文内的一个叫做 Set-Cookie 的首部字段信息,通知客户端保存 Cookie。当下次客户端再往该服务器发送请求时,客户端会自动在请求报文中加入 Cookie 值后发送出去。
服务器端发现客户端发送过来的 Cookie 后,会去检查究竟是从哪一个客户端发来的连接请求,然后对比服务器上的记录,最后得到之前的状态信息。
没有 Cookie 信息状态下的请求:
存有 Cookie 信息状态的请求:
三、HTTP 报文内的 HTTP 信息
用于 HTTP 协议交互的信息被称为 HTTP 报文。 客户端的 HTTP 报文叫做请求报文,服务器端的叫做响应报文。HTTP 报文本身是由多行(用 CR+LF 作换行符)数据构成的 字符串文本。
请求报文和响应报文的首部内容由以下数据组成:
-
请求行
- 包括用于请求的方法,请求 URI 和 HTTP 版本
-
状态行
- 包含表明响应结果的状态码,原因短语和 HTTP 版本
-
首部字段
- 包含表示请求和响应的各种条件和属性的各类首部
- 一般有 4 种首部
- 通用首部
- 请求首部
- 响应首部
- 实体首部
-
其他
- 可能包含 HTTP 的 RFC 里未定义的首部(Cookie 等)
HTTP 在传输数据时可以按照数据原貌直接传输,但也可以在传输过程中通过编码提升传输速率。通过在传输时编码,能有效的处理大量的访问请求。但是,编码的操作需要计算机来完成,因此会消耗更多的 CPU 等资源。
编码提升传输效率
-
报文(message)
- 是 HTTP 中通信的基本单位,由 8 位组字节流组成,通过 HTTP 通信传输
-
实体(entity)
- 作为请求或响应的有效载荷数据(补充项)被传输,其内容由实体首部和实体主体组成
HTTP 报文的主体用于传输请求或响应的实体主体。
通常,报文主体等于实体主体,只有当传输中进行编码操作时,实体主体的内容发生变化,才导致它和报文主体产生差异。
向待发送邮件内增加附件时,为了使邮件容量变小,我们会先用 ZIP 压缩文件之后再添加附件发送。HTTP 协议中有一种被称为 内容编码 的功能也能进行类似的操作。
内容编码指明应用在实体内容上的编码格式,并保持实体信息原样压缩。内容编码后的实体由客户端接收并负责解码。
在 HTTP 通信过程中,请求的编码实体资源尚未全部传输完成之前,浏览器无法显示请求页面。在传输大容量数据时,通过把数据分割成多块,能够让浏览器逐步显示页面。
这种把实体主体分块的功能称为 分块传输编码。
发送多种数据的多部分对象集合
发送邮件时,我们可以在邮件里写入文字并添加多份附件。这是因为采用了 MIME(Multipurpose Internet Mail Extensions, 多用途因特网邮件扩展) 机制,它允许邮件处理文本、图片、视频等多个不同类型的数据。
相应的,HTTP 协议中也采纳了多部分对象集合,发送的一份报文主体内可含有多类型实体。通常是在图片或文本文件等上传时使用。
- multipart/from-data
- Web 表单文件上传时使用
- multipart/byteranges
- 响应报文包含了多个范围的内容时使用
获取部分内容的范围请求
以前,用户不能使用现在这种高速的宽带访问互联网,当时,下载一个尺寸稍大的图片或文件就已经很吃力了。如果下载过程中遇到网络中断的情况,那就必须重新开始。为了解决上述问题,需要一种 可恢复 的机制,所谓恢复是指 能从之前下载中断处恢复下载。
要实现该功能需要指定下载的实体范围,像这样,指定范围发送的请求叫做范围请求(Range Request)。
如果服务器端无法响应范围请求,则会返回状态码 200 OK 和完整的实体内容。
内容协商返回最合适的内容
当浏览器的默认语言为英文或中文,访问相同 URI 的 Web 页面时,则会显示对应的英文版或中文版的 Web 页面,这样的机制称为 内容协商(Content Negotiation)。
内容协商机制是指客户端和服务器端就响应的资源内容进行交涉,然后提供给客户端嘴和适合的资源。内容协商会以响应资源的语言、字符集、编码方式等作为判断的基准。
四、返回结果的 HTTP 状态码
HTTP 状态码负责表示客户端 HTTP 请求的返回结果、标记服务器端的处理是否正常,通知出现的错误等工作。
(看到 5 开头的就可以找服务端了)。
五、与 HTTP 协作的 Web 服务器
一台 Web 服务器可搭建多个独立域名的 Web 网站,也可作为通信路径上的中转服务器提升传输效率。
HTTP/1.1 规范允许一台 HTTP 服务器搭建多个 Web 站点。比如,提供 Web 托管服务(Web Hosting Service)的供应商,可以用一台服务器为多位客户服务,也可以以每位客户持有的域名运行各自不同的网站。这是因为利用了 虚拟主机(Virtual Host,又称虚拟服务器) 的功能。
即使物理层面只有一台服务器,但只要使用虚拟主机的功能,则可以假想已具有多台服务器。
在相同的 IP 地址下,由于虚拟主机可以寄存多个不同主机名和域名的 Web 网站,因此在发送 HTTP 请求时,必须在 Host 首部内完整指定主机名或域名 URI。
通信数据转发程序:代理、网关、隧道
-
代理
- 一种有转发功能的应用程序,扮演了位于服务器和客户端 "中间人" 的角色,接收由客户端发送的请求并转发给服务器,同时也接收服务器返回的响应并转发给客户端。
-
网关
- 网关是转发其他服务器通信数据的服务器,接收从客户端发送来的请求时,它会像自己拥有资源的源服务器一样对请求进行处理。有时客户端可能都不会察觉,自己的通信目标是一个网关。
-
隧道
- 隧道是在相隔甚远的客户端和服务器两者之间进行中转,并保持双方通信连接的应用程序。
代理
代理服务器的基本行为就是接收客户端发送的请求后转发给其他服务器。代理不改变请求 URI,会直接发送给前方持有资源的目标服务器。
每次通过代理服务器转发请求或响应时,会追加写入 Via 首部信息以标记出经过的主机信息。
使用代理服务器的理由有:
- 利用缓存技术减少网络带宽的流量
- 组织内部针对特定网站的访问控制
- 以获取访问日志为主要目的
- ...
缓存代理(Caching Proxy):预先将资源的副本(缓存)保存在代理服务器上。
透明代理:转发时,不对报文做任何加工。反之,对报文内容进行加工的代理被称为非透明代理。
网关
网关的工作机制和代理十分相似,而网关能使通信线路上的服务器提供非 HTTP 协议服务。
利用网关能提高通信的安全性,因为可以在客户端与网关之间的通信线路上加密以确保连接的安全。比如,网关可以连接数据库,使用 SQL 语句查询数据。另外,在 Web 购物网站上进行信用卡结算时,网关可以和信用卡结算系统联动。
隧道
隧道可按要求建立起一条与其他服务器的通信线路,届时使用 SSL 等加密手段进行通信。隧道的目的是确保客户端能与服务器进行安全的通信。
隧道本身不会去解析 HTTP 请求。也就是说,请求保持原样中转给之后的服务器。隧道会在通信双方断开连接时结束。
通过隧道的传输,可以和远距离的服务器安全通信。隧道本身是透明的,客户端不用在意隧道的存在。
保存资源的缓存
缓存是指代理服务器或客户端本地磁盘内保存的资源副本。利用缓存可减少对源服务器的访问,因此也就节省了通信流量和通信时间。
即便缓存服务器内有缓存,也不能保证每次都会返回对同资源的请求,因为这关系到被缓存资源的 有效性 问题。
即使存在缓存,也会因为客户端的要求、缓存的有效期等因素,向源服务器确认资源的有效性。若判断缓存失效,缓存服务器将会再次从源服务器上获取 "新" 资源。
缓存不仅可以存在于缓存服务器内,还可以存在客户端浏览器中。
另外,和缓存服务器相同的一点是,当判断缓存过期后,会向源服务器确认资源的有效性。若判断浏览器缓存失效,浏览器会再次请求新资源。
六、HTTP 首部
HTTP 协议的请求和响应报文中必定包含 HTTP 首部,只是我们平时在使用 Web 的过程中感受不到它。首部内容为客户端和服务器分别处理请求和响应提供所需要的信息。
使用首部字段是为了给浏览器和服务器提供报文主体大小、所使用的语言、认证信息等内容。
4 种 HTTP 首部字段类型:
- 通用首部字段(General Header Fields)
- 请求报文和响应报文都会使用的首部
- 请求首部字段(Request Header Fields)
- 客户端发送请求时使用的首部,补充了请求的附加内容、客户端信息、响应内容相关优先级等信息
- 响应首部字段(Response Header Fields)
- 服务端返回响应报文时使用的首部,补充了响应的附加内容,也会要求客户端附加额外的内容信息
- 实体首部字段(Entity Header Fields)
- 针对请求报文和响应报文的实体部分使用的首部,补充了资源内容更新时间等与实体有关的信息
HTTP/1.1 规范定义了如下 47 种首部。
通用首部字段:
请求首部字段:
响应首部字段:
实体首部字段:
为 Cookie 服务的首部字段
七、确保 Web 安全的 HTTPS
在 HTTP 协议中有可能存在信息窃听或身份伪装等安全问题,使用 HTTPS 通信机制可以有效防止这些问题。
HTTP 有很多优点,也有一些不足,主要体现为:
- 通信使用明文(不加密),内容可能会被窃听
- 不验证通信方的身份,因此有可能遭遇伪装
- 无法证明报文的完整性,所以有可能已遭篡改
这些问题不仅在 HTTP 上出现,其他未加密的协议中也会存在这类问题。
通信使用明文可能被窃听
由于 HTTP 本身不具备加密的功能,所以也无法做到对通信整体(使用 HTTP 协议通信的请求和响应的内容)进行加密。
互联网上的任何角落都存在通信内容被窃听的风险。
窃听相同段上的通信并非难事,只需要收集在互联网上流动的数据包(帧)就行了。对于收集来的数据包的解析工作,可交给那些抓包(Packet Capture)或嗅探器(Sniffer)工具。
在目前大家正在研究的如何防止窃听保护信息的几种对策中,最为普及的就是加密技术,加密的对象可以有这么几个:
- 通信的加密
- 通过和 SSL 或 TLS 的组合使用,加密 HTTP 的通信内容
- 用 SSL 建立安全的通信线路之后,就可以在这条线路上进行 HTTP 通信了,与 SSL 组合使用的 HTTP 被称为 HTTPS(HTTP Secure,超文本传输安全协议)或 HTTP over SSL。
- 内容的加密
- 把 HTTP 报文里所含的内容进行加密处理
- 这种情况下,客户端需要对 HTTP 报文进行加密处理后再发送请求
- 为了做到有效的内容加密,前提是要求客户端和服务器同时具备加密和解密机制
- 由于该方式不同于 SSL 或 TLS 将整个通信线路加密处理,所以内容仍有被篡改的风险
不验证通信方的身份就可能遭遇伪装
HTTP 协议的实现本身非常简单,不论是谁发送过来的请求都会返回响应,因此不确认通信方,会存在以下各种隐患:
- 无法确定服务器是否是伪装的 Web 服务
- 无法确定客户端是否是伪装的客户端
- 无法确定正在通信的双方是否已具备访问权限
- 无法判定请求是来自何方、出自谁手
- 即使是无意义的请求也会照单全收,无法阻止海量请求下的 DoS 攻击(Denial of Service,拒绝服务攻击)
虽然使用 HTTP 协议无法确定通信方,但如果使用 SSL 则可以。SSL 不仅提供加密处理,而且还使用了一种被称为证书的手段,可用于确定通信方。
证书由值得信赖的第三方机构颁发,用以证明服务器和客户端是实际存在的。另外,伪造证书从技术角度来说是异常困难的一件事。所以只要能够确认通信方(服务器或客户端)持有的证书,即可判断通信方的真实意图。
无法证明报文完整性,可能已遭篡改
所谓完整性是指信息的准确度,若无法证明其完整性,通常也就意味着无法判断信息是否准确。
像这样,请求或响应在传输途中,遭攻击者拦截并篡改内容的攻击称为中间人攻击(Man-in-the-Middle attack,MITM)。
为了防止这些弊端,有必要使用 HTTPS。SSL 提供认证和加密处理及摘要功能。仅靠 HTTP 确保完整性是非常困难的,因此通过和其他协议组合来实现这个目标。
HTTP + 加密 + 认证 + 完整性保护 = HTTPS
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 是当今世界上应用最为广泛的网络安全技术。
相互交换密钥的公开密钥加密技术
SSL 采用一种叫做 公开密钥加密(Public-key cryptography) 的加密处理方式。
近代的加密方法中加密算法是公开的,而密钥却是保密的。通过这种方式得以保持加密方法的安全。
加密和解密都会用到密钥,没有密钥就无法对密码解密,反过来说,任何人只要持有密钥就能解密了,如果密钥被攻击者得到,那加密也就失去了意义。
加密和解密同用一个密钥的方式称为共享加密(Common key crypto system),也被叫做对称密钥加密。
以共享密钥方式加密时必须将密钥也发给对方,可究竟怎样才能安全的转交?在互联网上转发密钥时,如果通信被监听那么密钥就可能会落入攻击者之手,同时也就失去了加密的意义。另外还得设法安全的保管接收到的密钥。
发送密钥就有被窃听的风险,但不发送,对方就不能解密。再说,密钥若能够安全发送,那数据也应该能安全送达。
公开密钥加密方式很好的解决了共享密钥加密的困难。
公开密钥加密使用一对非对称的密钥,一把叫做私有密钥(private key),另一把叫做公开密钥(public key)。 顾名思义,私有密钥不能让其他任何人知道,而公开密钥则可以随意发布,任何人都可以获得。
使用公开密钥加密方式,发送密文的一方使用对方的公开密钥进行加密处理,对方收到被加密的信息后,再使用自己的私有密钥进行解密。利用这种方式,不需要发送用来解密的私有密钥,也不必担心密钥被攻击者窃听而盗走。
另外,要想根据密钥和公开密钥,恢复到信息原文是异常困难的,因为解密过程就是在对离散对数进行求值,这并非轻而易举就能办到。退一步讲,如果能对一个非常大的整数做到快速的因式分解,那么密码破解还是有希望的,但就目前的技术来看是不太现实的。
HTTPS 采用共享密钥加密和公开密钥加密两者并用的混合加密机制。若只考虑实现安全交换,那么仅使用公开密钥加密来通信就好了,但是公开密钥加密比共享密钥加密的处理速度要慢。
所以应充分利用两者各自的优势,在交换密钥环节使用公开密钥加密方式保证安全,之后的建立通信交换报文阶段则使用共享密钥加密方式保证效率。
遗憾的是,公开密钥加密方式还是存在一些问题的,那就是 无法证明公开密钥本身就是货真价实的公开密钥。比如,正准备和某台服务器建立公开密钥加密方式下的通信时,如何证明收到的公开密钥就是原本预想的那台服务器发行的公开密钥。或许在公开密钥传输途中,真正的公开密钥已经被攻击者替换掉了。
为了解决上述问题,可以使用由 数字证书认证机构(CA,Certificate Authority) 和其相关机关颁发的 公开密钥证书。
数字证书认证机构处于客户端与服务器双方都信赖的第三方机构的立场上。
数字证书认证机构的业务流程:
-
服务器的运营人员向数字证书认证机构提出公开密钥的申请
-
数字证书认证机构在判明出申请者的身份之后,会用自己的私有密钥对已申请的公开密钥做数字签名,将其和公钥证书绑定,并颁发给服务端
-
服务器会将这份由数字证书认证机构颁发的公钥证书发送给客户端
-
接到证书的客户端可使用数字证书认证机构的公开密钥,对那张证书上的数字签名进行认证,一旦验证通过,客户端便可明确两件事:
- 认证服务器的公开密钥是真实有效的数字证书认证机构
- 服务器的公开密钥是值得信赖的
-
使用服务器的公开密钥对报文加密后发送
-
服务器使用私有密钥对报文进行解密
此处认证机关的公开密钥必须安全的转交给客户端,使用通信方式时,如何安全转交是一件很困难的事。因此,多数浏览器开发商发布版本时,会 事先在内部植入 常用认证机关的公开密钥。
证书的一个作用是用来证明通信一方的服务器是否规范,另外一个作用是可确认服务器背后运营的企业是否真实存在,拥有该特性的证书就是 EV SSL 证书(Extended Validation SSL Certificate)。
客户端证书
HTTPS 中还可以使用客户端证书,以客户端证书进行客户端认证,证明服务器正在通信的对方是预料之内的客户端,其作用跟服务器证书如出一辙。
但客户端仍存在几处问题点:
-
证书的获取及发布
- 想获取证书时,用户得自行安装客户端证书。由于客户端证书是要付费购买的,且每张证书对应到每位用户也就意味着需支付和用户数对等的费用
- 另外,要让知识层次不同的用户们自行安装证书,这件事本身也充满了各种挑战
-
客户端证书只能用来证明客户端实际存在,而不能证明用户本人的真实有效性。也就是说,只要获得了安装有客户端证书的计算机的使用权限,也就意味着同时拥有了客户端证书的使用权限
现状是,安全性极高的认证机构可颁发客户端证书但仅用于特殊用途的业务,比如那些可支撑客户端证书支付费用的业务。
例如,银行的网上银行就采用了客户端证书,在登录网银时不仅要求用户确认输入 ID 和密码,还会要求用户的客户端证书,以确认用户是否从特定的终端访问网银。
由自认证机构颁发的证书称为自签名证书
独立构建的认证机构叫做自认证机构,由自认证机构颁发的 "无用" 证书也被戏称为自签名证书。
浏览器访问该服务器时,会显示 "无法确认连接安全性" 或 "该网站的安全证书存在问题" 等警告信息。
由自认证机构颁发的服务器证书之所以不起作用,是因为它无法消除伪装的可能性。自认证机构能够产生的作用顶多就是自己对外宣称 "我是 oo" 的程度。即使采用自签名证书,通过 SSL 加密之后,可能偶尔还会看见通信处在安全状态的提示,可那也是有问题的。因为就算加密通信,也不能排除正在和已经过伪装的加服务器保持通信。
中级认证机构
多数浏览器会预先植入备受信赖的认证机构的证书,但也有一小部分浏览器会植入中级认证机构的证书。
对于中级认证机构办法的服务器证书,某些浏览器会以正规的证书来对待,可有的浏览器会当做自签名证书。
HTTPS 的安全通信机制
为了更好的理解 HTTPS,我们来观察一下 HTTPS 的通信步骤:
- 客户端通过发送 Client Hello 报文开始 SSL 通信。报文中包含客户端支持的 SSL 指定版本、加密组件(Cipher Suite)列表(所使用的加密算法及密钥长度等)。
- 服务器可进行 SSL 通信时,会以 Server Hello 报文作为应答。和客户端一样,在报文中包含 SSL 版本以及加密组件。服务器的加密组件内容是从接收到的客户端加密组件内筛选出来的。
- 之后服务器发送 Certificate 报文,报文中包含公开密钥证书。
- 最后服务器发送 Server Hello Done 报文通知客户端,最初阶段的 SSL 握手协商部分结束。
- SSL 第一个握手结束之后,客户端以 Cline Key Exchange 报文作为回应。报文中包含通信加密中使用的一种称为 Pre-master secret 的随机密码串。该报文已用步骤 3 中的公开密钥进行加密。
- 接着客户端继续发送 Change Cipher Spec 报文。该报文会提示服务器,在此报文之后的通信会采用 Pre-master secret 密钥加密。
- 客户端发送 Finished 报文,该报文包含连接至今全部报文的整体校验值。这次握手协商是否能够成功,要以服务器是否能够正确解密该报文作为判定标准。
- 服务器同样发送 Change Cipher Spec 报文。
- 服务器同样发送 Finished 报文。
- 服务器和客户端的 Finished 报文交换完毕之后,SSL 连接就算建立完成。当然,通信会受到 SSL 的保护,从此处开始的应用层协议的通信,即发送 HTTP 请求。
- 应用层协议通信,即发送 HTTP 响应。
- 最后由客户端断开连接。断开连接时,发送 close_notify 报文
在以上流程中,应用层发送数据时会附加一种叫做 MAC(Message Authentication Code)的报文摘要。MAC 能够查知报文是否遭到篡改,从而保护报文的完整性。
HTTPS 存在的问题
HTTPS 也存在一些问题,那就是当使用 SSL 时,它的处理速度会变慢。它的慢分两种:
- 通信慢
- 和使用 HTTP 相比,网络负载可能会变慢 2 到 100 倍。除去和 TCP 连接、发送 HTTP 请求/响应 以外,还必须进行 SSL 通信,因此整体上处理通信量不可避免会增加。
- 消耗大量 CPU 及内存等资源,导致处理速度慢
- SSL 必须进行加密处理。在服务器和客户端都需要进行加密和解密的运算处理。因此从结果上来讲,比起 HTTP 会更多的消耗服务器和客户端的硬件资源,导致负载增强。
针对速度变慢这一问题,并没有根本性的解决方案,我们会使用 SSL 加速器这种(专用服务器)硬件来改善该问题。该硬件为 SSL 通信专用硬件,相对软件来说,能够提高数倍 SSL 的计算速度。仅在 SSL 处理时发挥 SSL 加速器的功效,以分担负载。
为什么不一直使用 HTTPS?
- 加密通信会消耗更多的 CPU 及内存资源
- 得买证书,证书得花钱
八、确认访问用户身份的认证
计算机本身无法判断坐在显示器前的使用者的身份。为了弄清楚是谁在访问服务,就得让对方的客户端自报家门。
为了确认是本人真的具有访问系统的权限,就需要核对 "登录者本人才知道的信息"、"登录者本人才会有的信息"。核对的信息通常是指以下这些:
- 密码
- 动态令牌
- 数字证书
- 生物认证
- IC 卡等
但是,即便对方是假冒的用户,只要能通过用户验证,那么计算机就会默认是出自本人的行为。因此,掌控机密信息的绝不能让他人得到,更不能轻易的就被破解出来。
HTTP 使用的认证方式:
- BASIC 认证(基本认证)
- 使用 Base64 对用户 ID 和密码进行编码后传输,但是不是加密处理,容易被解码
- 使用上不够灵活,且达不到多数 Web 网站期望的安全性等级,因此它并不常用
- DIGEST 认证(摘要认证)
- 提供防止密码被窃听的保护机制,但并不存在防止用户伪装的保护机制
- 与 BASIC 认证一样,使用上不灵活,也仍达不到多数 Web 网站对高度安全等级的追求标准,适用范围有限
- SSL 客户端认证
- 一般会和 FormBase 认证组合形成一种双因素认证,就是指,认证过程中不仅需要密码这一个因素,还需要申请认证者提供其他持有信息
- 需要用到客户端证书,需要支付一定费用才能使用
- FormBase 认证(基于表单认证)
- 客户端会向服务器上的 Web 应用程序发送登录信息(Credential),按登录信息的验证结果认证。
- 但是表单认证不具备共同标准规范,在每个 Web 网站都会有各不相同的实现方式。在表单认证的实现中存在问题的 Web 网站也是屡见不鲜。
基于表单认证的标准规范尚未有定论,一般会用 Cookie 来管理 Session(会话)。
基于表单认证本身是通过服务器端的 Web 应用,将客户端发送过来的用户 ID 和密码与之前登录过的信息做匹配来进行认证的。
但 HTTP 是无状态协议,因此已认证成功的用户状态无法通过协议层面保存下来,所以该用户下次访问时,也无法将其和其他用户区分开来。于是我们使用 Cookie 来管理 Session,以弥补 HTTP 协议中不存在的状态管理功能。
- 客户端把用户 ID 和密码等登录信息放入报文的实体部分,通常是以 POST 方法把请求发送给服务器。而这时,会使用 HTTPS 通信来进行 HTML 表单画面的显示和用户输入数据的发送。
- 服务器会发放用以识别用户的 Session ID。通过验证从客户端发送过来的登录信息进行身份认证,然后把用户的认证状态与 Session ID 绑定后记录在服务器端。
- 客户端接收到从服务器端发来 Session ID 后,会将其作为 Cookie 保存在本地。下次向服务器发送请求时,浏览器会自动发送 Cookie,所以 Session ID 也随之发送到服务器。服务器可通过验证接收到的 Session ID 识别用户和其认证状态。
你可以把 Session ID 想象成一种用以区分不同用户的等位号。
如果 Session ID 被第三方盗走,对方就可以伪装成你的身份进行恶意操作了。因此必须防止 Session ID 被盗,或被猜出。
一种安全的保存方法是,先利用给密码加盐(salt)的方式增加则外信息,再使用散列(hash)函数计算出散列值后保存。但是我们也经常看到直接保存明文密码的做法,而这样的做法具有导致密码泄露的风险。
九、基于 HTTP 的功能追加协议
虽然 HTTP 协议既简单又便捷,但随着时代的发展,其功能使用上捉襟见肘的疲态已经凸显。
HTTP 功能上的不足可通过创建一套全新的协议来弥补。但是目前基于 HTTP 的 Web 浏览器的使用环境已遍布全球,因此无法完全抛弃 HTTP。有一些新协议的规则是基于 HTTP 的,并在此基础上添加了新的功能。
在 Facebook 和 Twitter 等 SNS 网站上,几乎能够实时观察到海量用户公开发布的内容,Web 网站为了保存这些新增内容,在很短的时间内就会发生大量的内容更新。
为了尽可能实时的显示这些更新的内容,服务器上一有内容更新,就需要直接把这些内容反馈到客户端的界面上。虽然看起来挺简单的,但 HTTP 却无法妥善的处理好这项任务。
使用 HTTP 协议探知服务器上是否有内容更新,就必须频繁的从客户端到服务器端进行确认。如果服务器上没有内容更新,那么就会产生徒劳的通信。
若想在现有 Web 实现所需的功能,以下 HTTP 标准就会称为瓶颈:
- 一条连接只可发送一个请求
- 请求只能从客户端开始,客户端不可以接收除了响应以外的指令
- 请求/响应 首部未经压缩就发送,首部信息越多延迟越大
- 发送冗长的首部,每次互相发送相同的首部造成的浪费较多
- 可任意选择数据压缩格式,非强制压缩发送
使用浏览器进行全双工通信的 WebSocket
其实就是我们所说的长链接。
一旦 Web 服务器与客户端之间建立起 WebSocket 协议的通信连接,之后所有的通信都依靠这个专用协议进行。通信过程中可互相发送 JSON、XML、HTML 或图片等任意格式的数据。
由于是建立在 HTTP 基础上的协议,因此连接的发起方仍是客户端,而一旦建立 WebSocket 通信连接,不论是服务器还是客户端,任意一方都可直接向对方发送报文。
WebSocket 的主要特点:
- 推送功能
- 服务器可直接发送数据到客户端
- 减少通信量
- 建立之后一直保持在连接状态,WebSocket 的首部信息也很小
十、构建 Web 内容的技术
HTML
Web 页面几乎全由 HTML 构建。
HTML(HyperText Markup Language,超文本标记语言)是为了发送 Web 上的超文本(Hypertext)而开发的标记语言。超文本是一种文档系统,可将文档中任意位置的信息与其他信息(文本或图片等)建立关联,即超链接文本。标记语言是指通过在文档的某部分穿插特别的字符串标签,用来修饰文档的语言。我们把出现在 HTML 文档里的这种特殊字符串叫做 HTML 标签(Tag)。
平时我们浏览的 Web 页面几乎全是使用 HTML 携程的。由 HTML 构成的文档经过浏览器的解析、渲染后,呈现出来的结果就是 Web 页面。
CSS(Cascading Style Sheets,层叠样式表)可以指定如何展现 HTML 内的各种元素,属于样式表标准之一。即使是相同的 HTML 文档,通过改变应用的 CSS,用浏览器看到的页面也会随之改变。CSS 的理念就是让文档的结构和设计分离,达到解耦的目的。
动态 HTML
所谓动态 HTML(Dynamic HTML),是指用客户端脚本语言将静态的 HTML 内容编程动态的技术的总称。
动态 HTML 技术是通过调用客户端脚本语言 JavaScript,实现对 HTML 的 Web 页面的动态改造。利用 DOM(Document Object Model,文档对象模型)可指定于发生动态变化的 HTML 元素。
DOM 是用以操作 HTML 文档和 XML 文档的 API。使用 DOM 可以将 HTML 内的元素当做对象操作,如取出元素内的字符串、改变哪个 CSS 的属性等,使页面的设计发生变化。
XML(eXtensible Markup Language,可扩展标记语言)是一种可按目标进行扩展的通用标记语言。旨在通过使用 XML,使互联网数据共享变得更容易。
从 XML 文档中读取数据比起 HTML 更为简单。因为 XML 的结构基本上都是用标签分割而成的树形结构,因此通过语法分析器(Parser)的解析功能解析 XML 结构并取出数据元素,可更容易的对数据进行读取。
RSS(简易信息聚合,也叫聚合内容)和 Atom 都是发布新闻或博客日志等更新信息文档的格式的总称。两者都用到了 XML。
JSON(JavaScript Object Notation)是一种以 JavaScript(ECMAScript)的对象表示法为基础的轻量级数据标记语言。能够处理的数据类型有 false/null/true/数组/数字/字符串,这 7 种类型。
JSON 让数据更轻更纯粹,并且 JSON 的字符串形式可被 JavaScript 轻易的读入。当初配合 XML 使用的 Ajax 技术也让 JSON 的应用变得更为广泛。另外,其他各种编程语言也提供丰富的库类,以达到轻便操作 JSON 的目的。
后记和补充
还有一章是关于 Web 的攻击技术的,这里就不提了,感兴趣的可以去看原书。
另外做一点补充,一般我们项目中并不需要去处理 HTTPS 连接相关的东西,是因为网络通讯层已经帮我们做了这个事情,比如 AFN,这些第三方类库已经帮我们处理好了。
苹果已经封装了HTTPS连接的建立、数据的加密解密功能,我们直接可以访问https网站的,但苹果并没有验证证书是否合法,无法避免中间人攻击。要做到真正安全通讯,需要我们手动去验证服务端返回的证书。AFNetwork中的AFSecurityPolicy模块主要是用来验证HTTPS请求时证书是否正确。AFSecurityPolicy封装了证书验证的过程,让用户可以轻易使用,除了去系统信任CA机构列表验证,还支持SSL Pinning方式的验证。
TCP 的三次握手和四次挥手
三次握手
首先来看三次握手:
TCP 三次握手,其实就是建立了一个 TCP 连接,客户端与服务器交互需要 3 个数据包。握手的主要作用就是为了确认双方的接收和发送能力是否正常、初始序列号、交换窗口大小以及 MSS 等信息。
- 第一次握手
- 客户端发送
SYN报文,并进入SYN_SENT状态,等待服务器的确认
- 客户端发送
- 第二次握手
- 服务器收到
SYN报文,需要给客户端发送ACK确认报文,同时服务器也要向客户端发送一个SYN报文,所以也就是向客户端发送SYN + ACK报文,此时服务端进入SYN_RCVD状态
- 服务器收到
- 第三次握手
- 客户端收到
SYN + ACK报文,向服务器发送确认包,客户端进入ESTABLISHED状态。待服务器收到客户端发送的ACK包也会进入ESTABLISHED状态
- 客户端收到
TCP 三次握手,其实就是 TCP 应用在发送数据前,通过 TCP 协议跟通信双方协商好连接信息,建立起 TCP 的连接关系。
为什么需要三次握手?二次不行吗?
TCP 建立连接之前,需要确认的是客户端与服务器双方的手包和发包的能力。
- 第一次握手
- 客户端发送网络包,服务端收到了。服务端可以得出结论:客户端的发送能力,服务端的接收能力是正常的。
- 第二次握手
- 服务端发包,客户端收到了。客户端就可以得出结论:服务的接收、发送能力,客户端的接收、发送能力是正常。不过此时服务端并不能确认客户端的接收能力是否正常。
- 第三次握手
- 客户端发包,服务端收到了。服务端可以得出结论:客户端的接收、发送能力正常,服务端自己的发送、接收能力也正常。
所以需要三次握手才能确认双方的 接收 与 发送 能力是否正常。
四次挥手
- 第一次挥手
- 客户端发起
FIN包(FIN = 1),客户端进入FIN_WAIT_1状态。
- 客户端发起
- 第二次挥手
- 服务端收到
FIN包,发出确认包ACK,服务端进入了CLOSE_WAIT状态。这个时候客户端已经没有数据要发送了,不过服务端有数据发送的话,客户端依然需要接收。客户端接收到服务器发送的ACK之后,进入了FIN_WAIT_2状态。
- 服务端收到
- 第三次挥手
- 服务端数据发送完毕后,向客户端发送
FIN包,服务端此时进入了LAST_ACK状态
- 服务端数据发送完毕后,向客户端发送
- 第四次挥手
- 客户端收到服务端的
FIN包后,发出确认包,此时客户端就进入了TIME_WAIT状态。注意此时 TCP 连接还没有释放,必须经过2*MSL后,才进入CLOSE状态,可以看出服务器结束 TCP 连接的时间要比客户端早一些
- 客户端收到服务端的
为什么建立只需要三次握手,关闭时却需要四次挥手?
其实在 TCP 三次握手的时候,接收端发送的 SYN+ACK 的包是将一个 ACK 和一个 SYN 合并到一个包中,所以减少了一次包的发送,三次就完成了握手。
但是对于四次握手,由于 TCP 是全双工通信,在主动关闭方发送 FIN 包后,接收端可能还需要发送数据,不能立即关闭服务器端到客户端的数据通道,所以也就不能将服务器端的 FIN 包与对客户端的 ACK 包合并发送,只能先确认 ACK,然后服务端等无需发送数据时再发送 FIN 包,所以四次挥手时必须是进行四次数据包的交互。
为什么
TIME_WAIT状态需要经过2*MSL才能返回到CLOSE状态?
MSL 指的是报文在网络中传输的最大生存时间。在客户端发送对服务端的 FIN 的确认包 ACK 后,这个 ACK 包有可能是不可达的,服务端如果收不到 ACK 的话就需要重新发送 FIN 包。
所以客户端发送 ACK 后需要留出 2*MSL 时间(ACK 到达服务器 + 服务器发送 FIN 重传包,一来一回)等待确认服务端确实收到了 ACK 包。
也就是说客户端如果等待 2MSL 时间也没有收到服务端的重传包 FIN,说明可以确认服务器已经收到客户端发送的 ACK。
还有第 2 个理由,避免新旧连接混淆。
在客户端发送完最后一个 ACK 报文段后,在经过 2MSL 时间,就可以使本连接持续的时间内所产生的所有报文都从网络中消失,使下一个新的连接中不会出现这种旧的连接请求报文。
有些自作主张的路由器会缓存 IP 数据包,如果连接重用了,那么这些延迟收到的包就有可能会跟新连接混在一起。
TCP 和 UDP
差异:
- 连接性
- TCP 是面向连接的协议,在收发数据前必须和对方建立可靠的连接。
- UDP 是一个面向无连接的协议,数据传输前,源端和终端不建立连接
- 可靠性
- TCP 提供可靠交付的服务,传输过程中采用许多方法保证在连接上提供可靠的传输服务,如编号与确认,流量控制、计时器等,确保数据无差错,不丢失,不重复且按序到达
- UDP 使用尽可能最大努力交付,但不保证可靠交付
- 报文首部
- TCP 报文首部有 20 个字节,额外开销大
- UDP 首部只有 8 个字节,标题短,开销小
- TCP 协议面向字节流,将应用层报文看成一串无结构的字节流,分解为多个 TCP 报文段传输后,在目的站重新装配
- UDP 协议面向报文,不拆分应用层报文,只保留报文边界,一次发送一个报文,接收方去除报文首部后,原封不动的将报文交给上层应用
- 吞吐量
- TCP 拥塞控制、流量控制、重传机制、滑动窗口等机制保证传输质量
- UDP 没有
- 双工性
- TCP 只能点对点全双工通信
- UDP 支持一对一、一对多、多对一和多对多交互通信
SSL 和 TLS
TLS 实际上是 SSL 的更新版本,它修复了早期 SSL 协议中的一些安全漏洞。
- SSL 1.0 - 由于安全问题从未公开发布
- SSL 2.0 - 1995 年发布,2011 年弃用,存在已知的安全问题
- SSL 3.0 - 1996 年发布,2015 年弃用,存在已知的安全问题
- TLS 1.0 - 1999 年作为 SSL 3.0 的升级发布,计划在 2020 年弃用
- TLS 1.2 - 2008 年发布
- TLS 1.3 - 2018 年发布
关于抓包工具可以抓取 HTTPS 通信内容
这里我们以 Charles 为例,用过 Charles 都知道它不仅可以抓 HTTP 的包,而且还可以抓 HTTPS 的包,但前提是你得安装并信任它的证书。
这个抓包并解密的关键就是这个证书,当浏览器和服务器通信时,Charles 接收服务器的证书,但动态生成一张证书给浏览器(客户端),也就是说 Charles 作为中间代理在浏览器和服务器之间通信,所以通信的数据可以被 Charles 拦截并解密。由于 Charles 更改了证书,浏览器校验不通过会给出安全警告,必须安装 Charles 的证书后才能正常访问。
- 客户端向服务器发起 HTTPS 请求
- Charles 拦截客户端的请求,伪装成客户端向服务器进行请求
- 服务器向 "客户端"(实际上是 Charles)返回服务器的 CA 证书
- Charles 拦截服务器的响应,获取服务器证书公钥,然后自己制作一张证书,将服务器证书替换后发送给客户端。(这一步,Charles 拿到了服务器证书的公钥)
- 客户端接收到 "服务器"(实际上是 Charles)的证书后,生成一个对称密钥,用 Charles 的公钥加密,发送给 "服务器"(Charles)
- Charles 拦截客户端的响应,用自己的私钥解密对称密钥,然后用服务器证书公钥加密,发送给服务器。(这一步,Charles 拿到了对称密钥)
- 服务器用自己的私钥解密对称密钥,向 "客户端"(Charles)发送响应
- Charles 拦截服务器的响应,替换自己的证书后发送给客户端
- 至此,连接建立,Charles 拿到了服务器证书的公钥和客户端与服务器协商的对称密钥,之后就可以解密或者修改加密的报文了
简单来说就是 Charles 作为中间人代理,拿到了服务器的公钥证书和 HTTPS 连接的对称密钥,前提是客户端选择安装并信任 Charles 的 CA 证书,否则客户端就会 "报警" 并终止连接。如此看来,HTTPS 也算是安全的。
但是我们平时在开发时,如果是比较重要的接口,最好还是加密一下通信的内容,不要明文传输,这样才更加保险。
OVER~