对HTTP的一些理解以及对应用层其他协议的讨论(一)

  一个多月,陆陆续续看完了这本《Http权威指南》,对HTTP和整个TCP/IP协议栈也算是有了初步了解,想写一篇博客谈谈HTTP协议并引申出对应用层一些其他协议的讨论。

  HTTP先从协议和URL讲起吧,算是一些HTTP协议的前置知识。

  首先是网络协议,我们都知道计算机通过网线等设备传递的本质上知识0101的比特,那么总得有一个规范规定什么样的比特集可以表示什么样的信息,以及传输的时候以什么样的方式传输,这就是协议的用处,最早的协议模型是1985年ISO组织提出的OSI7层模型,用于指导公司要以什么样的形式控制网络,然而那种架构比较复杂,后来出现的TCP/IP协议栈简化了这种7层模型,由于现在普遍参考的都是TCP/IP协议栈,在这里我只介绍TCP/IP协议栈吧

    TCP/IP是一个协议集,他的名字来源于其中最重要的两个协议:TCP协议和IP协议,对英特网中主机的寻址方式,主机的命名机制,信息的传输规则,以及各种服务功能作了约定.,他从下到上分为五层:物理层,数据链路层,网络层,传输层,应用层(有一些分法是把数据链路层和物理层合为一层,whatever),下面逐层讲解各层是做什么的:

   物理层:这一层考虑的主要是怎样才能在连接各种计算机的传输媒体上传输数据比特流,而不是具体的传输媒体,我们都知道现有的计算机网络中的硬件设备和传输媒体的种类非常多,而物理层作用就是屏蔽掉这些传输媒体和通信手段的差异,使得他上面的数据链路层感觉不到这些差异,这样就可以是数据链路层只需考虑如何完成本层的协议和服务,而不必考虑网络具体的传输媒体和通信手段是什么,主要规定的是一下四点:1.接口形状尺寸,引脚排列等机械特性,2.电压范围等电气特性,3.电压表示含义等功能特性,4.不同功能出现顺序的时序特性。

  数据链路层:数据链路层主要做的是把网络层的数据封装成帧,并发送到链路上,以及吧链路上的帧中的数据取出并上交给网络层,这里解释一些链路和数据链路,链路指的是一个节点到相邻节点的一端物理线路(有线或者无线),而数据链路指的是在物理线路外,加一些必要的通信协议,若把实现了这些协议的硬件和软件加到链路上,就构成了数据链路,这里解决的主要就是封装成帧,透明传输,差错检测,流量控制,寻址等问题

  网络层:使用IP协议,也称为IP层,他向上(传输层)只提供简单灵活,无连接,尽最大努力交付的数据报服务,他从传输层获取TCP报文,并添加IP地址,通过ARP协议将IP地址转化为硬件地址便于数据链路层形成MAC帧,并通过ICMP协议进行高效转发IP数据报提高交付成功的机会,在这一层我们的基本数据单元是IP数据报

  传输层:主要是是TCP和UDP协议,功能是向上面的应用层提供通信服务,可以这么说:网络层是通过IP地址在主机之间提供逻辑通信,而运输层是通过端口号为应用进程之间提供端到端的逻辑通信

  应用层:我们已经知道可以通过传输层为应用进程提供端到端的通信服务,但是不同网络应用的应用进程之间还需要不同的通信规则,比如传输文件的网络应用要有对应的规则,传输万维网文档的网络应用要有对应的规则,而应用层就是设定各种协议为不同的网络应用服务的,主要协议有如DNS解析域名到IP地址的协议,传送文件的FTP协议和传送万维网文档的HTTP协议等,接下里要讲的HTTP协议就位于这一层。

  简单说下URL,URN,和URI:URI指的是统一资源标识符,URL和URN都是他的子集,不过现在我们说的URI大多数是指URL,URL是统一资源定位符,是明确说明如何从一个精确的固定的服务器获取资源的,像

http://www.joes-hardmware.com/specials/saw-blade.gif

就明确说明了从www.joes-hardware.com这个域名对应的IP服务器获取specials文件下的saw-blade.gif这个资源,而URN想得更深,他希望通过一个唯一的名称去标识一个资源,而这个资源与他的所在地无关,通过URN还可以用一个名字通过多种网络访问协议来访问资源,这是一个还在试验阶段的技术,还未大范围使用,所以我们说URI的时候基本上都是说URL,可以这么说:

  URN是URL的未来。

  说完了这些前置的知识,开始正式切入HTTP协议,我们先从HTTP报文的格式开始讲起。

  HTTP报文就是HTTP应用程序之间发送的数据块,我们用事务处理来描述一个HTTP报文从请求端发送,到服务端接受到服务端响应,再到客户端接到响应这个过程,用上下游来表示报文流动的方向,(比如报文从客户端发到服务端,那么我们称报文流流向下游,注意报文流不会像上游移动)。

  报文由三部分组成:起始行(requestline),首部(headers),还有主体(entiity-body),而对于请求报文和响应报文,彼此的报文格式是不相同的,但大致遵循这样的规范:

  请求报文:

 

 

 

 

  响应报文:

 

 

 

  其中请求报文的起始行说明要做什么,method是方法,表示希望服务器对资源执行的动作,像POST,GET,HEAD等,request-url表示我要请求的资源的url是什么 version表示我当前使用的HTTP版本,而响应报文起始行说明的是我做了什么,或者说发生了什么,其中status表示状态码,用来描述结果的状态如成功出错等,reason-phrase是为了可读性加入的描述性短语,用来描述这个状态码的,这个短语对报文传输本身是没有任何用的,单纯为了人类阅读而已。

  再看首部,首部是用来加入一些附加信息的,本质上只是一些键值对,比如Content-length:19,在报文中就是用来表示主体的文本长度为19,注意一下首部的每一行都是由一个CRLF结束的,而整个首部也是以一个CRLF结束的,CRLF可以理解成“回车”,(其实CRLF是两个ASCII码组成的,分别是CR和LF,CR表示是光标回滚到最左侧,LF表示光标移动到下一行,两者结合在一起基本上就是我们所知道的回车功能)

  主体:这个是一个可选部分,像有一些请求报文就没有主体,直接首部接一个CRLF报文就没了,主体就是HTTP要传输的内容,他可以是多种类型,二进制,文本之类的。

  其实上面只是对HTTP报文格式非常笼统的一个介绍,具体到细节的话还是挺繁多复杂的,像起始行中的方法就有好几种,《HTTP权威指南》从P51到P79介绍了许多首部中如方法,状态码类型的归纳,对于不常用的可以当字典一样查查就好。

  既然已经知道了HTTP报文是什么样的格式了,那么顺藤摸瓜,我们自然就会像知道HTTP报文要怎么通过传输层传输,怎样和对面建立连接,以及有什么样的方法可以提高传输的效率,这就是我们要讲的HTTP的第二个部分:连接和传输:
  由于HTTP的连接是通过TCP/IP承载的,那么我们就必须对TCP协议有一个比较大概的了解,首先TCP是通过三路握手进行连接,而且TCP协议中传输的基本数据单元是IP分组(IP数据报),TCP的传输是可靠而有序的,这里先介绍一下IP数据报:

  IP数据报由三个部分组成:IP分组首部,TCP段首部,TCP数据块,IP首部包含了源和目的的IP地址,长度和其他一些标记,TCP段首部是包括TCP端口号,TCP控制标记(SYN,ASK等)还有用于数据排序和完整性检查的一些数字值,在进行HTTP协议传输时发生了这样一个过程,首先请求报文中的起始行有一个request-line(上文讲到的),这里有一个应用层的协议叫DNS协议的会把他解析成IP地址和端口号,之后会通过三次握手发起到对应IP地址的对应端口号的连接,然后把这两个分别加上本机的IP地址和端口号一起加到IP分组的源IP地址、目的IP地址、源端口、目的端口中,然后再把首部加报文作为TCP数据块嵌入到IP分组中发送,再从服务器获得相应报文,之后四次挥手关闭连接,这样我们就大概知道了HTTP是如何通过TCP协议进行报文传输的了,接下来一个问题是如何提高传输的效率。

  对效率的思考必然会回到对HTTP时延的观察中,那么HTTP时延主要来源于哪里呢?人生苦短,直接上结论:

  1.根据URL确定IP和端口号的DNS解析时延

  2.建立TCP连接的时延

  3.读取报文,处理请求,以及发送响应报文的时延

  对HTTP的一些理解以及对应用层其他协议的讨论(一)_第1张图片

   如上图(颈椎病患者福音),不难发现HTTP传输的时延最主要的还是来自于TCP的相关时延,那么追究一下,具体是那些因素导致TCP传输会占用如此多的时延呢?最主要是以下5点:

  1.TCP建立的三次握手

  2.TCP慢启动拥塞控制

  3.数据聚集的Nagle算法

  4.用于捎带确认的TCP延迟确认算法

  5.TIME_WAIT时延和端口耗尽

  接下来逐一讲解这些时延是什么意思以及我们有什么解决的方法

  首先是握手时延,主要进行三次握手,第一次发送SYN报文,把TCP段首部的SYN标志位置为1,同时在TCP段序号(seq)写入一个值X,第二次是服务器发送一个SYN+ACK报文,把SYN置为1,ACK标记位置为X+1,令seq=Y,表示已经收到了报文,请求客户端去确认这个确认,最后是客户端发送一个ACK报文 ,把ACK置为Y+1,seq置为z表示收到了这个确认

               

(三次握手和四次握手经常被问到的问题基本就是少一路会怎样,为什么要来回几次这种)

  现代的TCP栈一般都允许客户端在第三次握手的时候发送数据,以减少时延,这里允许的原因是HTTP事务通常不会交换太多数据,而ACK分组通常足够大,大到允许我们把整个HTTP请求报文放进去,但是这样还是没办法解决一些根本问题:小的HTTP事务可能会在TCP建立上花费50%甚至更多的时间,那么我们就会想到能否通过重用现有的TCP连接去减少这种时延,这就涉及到了一个很重要的首部--connection首部。

  首先我们要知道客户端和服务器通常并非是直接相连的,他们之间可能存在多个HTTP中间实体,比如代理,高速缓存等,我我们HTTP报文流传递的时候是逐条的将HTTP报文经过这些中间设备,转发到远端服务器的(或者反向传输到客户端),那么这个时候我们可以在HTTP首部的connection首部字段去维护一个逗号分隔的连接标签列表,这些标签为此连接指定了一些不会传播到其他连接中去的选项,比如可以用connection:close来说明发送完吓一跳报文之后必须关闭这个连接(注意不会传播这个点)

  HTTP/1.0中connection有一个称为keep-alive的值用于建立持久的长连接(没错,是1.0,认为HTTP/1.1前没有持久连接是非常偏颇的说法),这时connection是默认这个值是不使用的,如果要建立一个长连接就需要在connection中加一个Keep-Alive,但是这样就会导致一个很严重的问题,就是盲中继,也就是不遵循不传播connection首部这个原则的中继,即时后来望网景公司试图用proxy-connection首部去解决这个问题依旧无法解决有多个盲中继的报文错误问题,具体细节这里不谈了,《HTTP权威指南》的P100-P103有详细的解释,只需要知道HTTP/1.1停止了对keep-alive的支持,转而使用一种名为persistent-connection的改进设计取代了他,他工作方式是默认就是激活持久连接的,如果要关闭,就在conncection中添加close字段,这里注意一点,HTTP/1.1之后客户端假定收到响应后,除非响应宝行了close,不如连接级维持在打开状态,但是,客户端和服务器仍然可以随时关闭空闲的连接,不发送close不意味着服务器承诺永远将连接保持在打开状态!

  (留一个值得思考的问题:为什么通过这样的设计就可以解决盲中继的问题)

  通过复用现存连接来减少多次握手讲完了,接下来讲讲TCP的另一个时延问题:延迟确认

  首先我们要知道IP网络层是不对分组的有序性和正确性负责的,如果网络路由器超负荷,那他会随意丢弃分组,这么一来,确认有序和正确性的任务及落到了TCP上,TCP在每一次接收到一个分组时,都会发送一个小的确认分组,如果发送者没有收到这个确认分组,就会认为分组被毁坏了,并且会重新发数据,但是由于确认报文很小,所以TCP会选择对他进行捎带,就是讲这些确认信息和输出的数据分组结合一起,用一个称为“延迟确认”的算法,在一个特定的窗口时间呃逆将输出放在缓冲区,如果在这个窗口时间都没有能捎带他的分组的话,就单独传送。

  但是要注意HTTP发送的频率是双峰聚集的,就是请求和应答时会大量传输,可能你希望有捎带他的分组时偏偏没有,这就造成了大量时延,这里我们可以选择通过对TCP配置进行修改去调整或者禁用延迟确认算法。

  接下来简单讲讲TCP慢启动问题和NAGLE算法,慢启动指的是TCP会随着时间进行慢慢调谐到达最大速度防止突然的过载和拥塞,我们可以通过之前讲过的“持久连接”解决这个问题,NAGLE算法是指TCP会趋向于让大量TCP数据绑定成一个分组一起发送,防止大量少数据的分组占用网络,然而小的HTTP报文可能永远无法填满一个分组,从而产生实验,这里我们可以对于小HTTP报文选择禁用Nagle算法

  最后讲讲TIME_WAIT累计和端口耗尽的问题:

  思考这样一个问题:假设客户端向服务端发送两个包 A和B,第一次发A成功,发B阻塞在某一个网络节点,于是客户端重发B包成功,第二次发两个包 C和D,C成功,准备发D包时,之前的B包不阻塞了,发到服务端,服务端不知道以为这是D包,GG!于是TCP为了解决这个问题设了一个值TIME_WAIT,通常为2分钟,表示一个已经关闭连接的端口在这个时间内一直保持关闭,但是对于现代的高速路由器来说,这种重复分组几乎不可能在几分钟后才出现服务器上,所以通常会把这个值设的小一点,即时这样,这对性能的影响也是非常大的,假设现在服务器有60000个端口,那么在120秒内连接无法重用的话就是说们的连接率就会被限制在60000/120=500次/秒,这就导致了我们必须要把服务器的连接率限制在500次/秒以下保证我们不会遇到TIME_WAIT端口耗尽问题,解决的比较好的方法就是去确保客户端和服务器在循环使用几个虚拟IP地址

 终于写完了关于连接的问题,但是我们要注意到上面关于连接的种种描述都只是针对一个客户端和相邻服务器而言的,之前讲到过客户端和服务器之间往往存在着许多中间实体,可以把这些中间实体都认为是特殊的服务器,最常见的就是代理,缓存以及各种集成点如网关、隧道和中继,下面对他们做一些比较粗略的介绍并着重谈谈网关

  代理:可以认为是客户端和服务器,他既是服务器有时客户端,他连接的必须是两个使用相同的协议的应用程序(这里区分网关),一般用来提高安全性(过滤广告之类),提高性能,节省费用,监控流量等

  缓存:可以认为是一个自动保存常见文档的HTTP设备,如果有Web请求到达缓存,而缓存已经有了这个副本的时候就可以直接把这个副本给请求,经常用于一些爆炸新闻,突发事件发生时对一些web文档进行缓存(这里常常涉及的一个问题就是文本的新鲜度计算算法以及何时替换旧的文档)

  网关:可以认为是一个翻译,之前讲到代理必须连接的是两个相同协议的应用程序,而网关可以连接两个不同协议的应用程序,在网关内部可以进行协议的转换,同时也可以请求其他类型的资源,比如数据库的一条记录之类的,之后会细讲

  隧道:位于客户端和网关之间,通过请求报文起始行的CONNECT方法建立,建立之后客户端可以在HTTP连接中嵌入非HTTP流量,让这类流量穿过只允许Web流量通过的防火墙

  中继:不完全遵循HTTP协议的代理,比如上面讲过的盲中继

  下面重点讲一讲网关:网关可以认为是一个粘合剂,或者说是一道门,门进去的是请求,出来的就是一个响应,比如你请求一个数据库的表,把起始行的URL写成/query-db.cgi?newproducts这种,那么网关就会想数据库发送查询语句获得对应的表返回给你,网关分为两类:协议网关和资源网关。

  协议网关:协议网关就是网关用来转化两种不同协议用的,比如客户端发送的是一个HTTP协议,但是请求的是一个FTP域名商的文件(就起始行的URL设置),那么网关就会自动根据HTTP报文发送FTP报文请求一个资源,然后又通过HTTP发送回客户端,协议网关可以细分为服务器端网关,表示通过HTTP与客户端对话,通过其他协议与其他服务器对话(HTTP/*)和客户端网关,通过其他协议和客户端对话,通过HTTP与服务器通信(*/HTTP),我们常见的有(HTTP/HTTPS)这种就是服务器端的安全网关,就是给输入web进行加密提供额外隐私和安全性保护的以及(HTTPS/HTTP)这种安全加速的网关。

  资源网关:资源网关比较常见的就是应用程序服务器,就是把目标服务器和网关结合起来,他通过HTTP和客户端通信,同时和服务器端的应用程序相连,这样讲很抽象,通俗点说就是一个客户端发了个请求给服务器,服务器不立刻回应,而是通过一个接口把请求转发给一个应用程序,再从这个接口获取信息,再通过HTTP发回给客户端,这里的接口就是指网关应用程序接口,也就是网关API

  提到网关API就不得不提CGI,这是第一个流行的网关API,CGI运行的本质大致可以归纳为这样一个过程:当一个请求来临,服务器发现他需要使用网关资源时,会引发一个网关进程,并把响应的数据(通常就是请求)传给这个进程,进程处理完数据后会返回一个响应给服务器,然后服务器再把响应返回给客户端,需要注意的是服务器进程和网关进程是独立的。

  接下来想讨论的是HTTP中一个比较重要的话题,就是身份认证和安全加密,首先是认证,我们都知道一个web服务器是会同时和多个客户端进行对话的,这些服务器要记录下他们是和谁对话,但是HTTP又是无状态的,那么我们就需要一些识别机制,主要方法有这么几种:(嫌长的可以直接看第五点 cookie......)

  1.我们可以在HTTP首部加入用户的身份信息,这些首部包括比如:

         From 用来描述用户的E-mail地址

        User-Agent 用来描述用户发送请求的浏览器软件

       Referer  用户是从哪个页面跳转过来的

       Authorization 用户名和密码

      Client-IP   客户端的IP地址

      X-Forwarded-For   也是用来描述客户端的IP地址

      cookie  服务器产生的用来描述一个特定客户端的标签(这个很重要)

其中前三个是不可靠的,不能实现一个可靠的识别,(想想也知道)。

2.追踪客户端的IP地址,这种和首部加入IP要区分,他不需要首部提供IP地址,可以自动追寻,比如在UNIX系统里面就有一个getpeername方法可以返回客户端地址,但是这样有几个不好的地方,比如你这样追寻的只是发请求的那台机器,而不是用户,如果多个用户用同一台机器呢?还有有一些ISP(因特网服务提供商)会动态为用户分配IP地址还有射击防火墙代理之类的问题就不细讲了

3.第三个可行的方法就是通过用户登录来查询这个用户是谁,用的就是第一个方法里面讲的Authenticate首部,这种方法的一个事务流程就是用户去请求一个服务器的URL,然后URL返回一个401状态码表示需要登录,同时在响应报文首部加一个WWW-authenticate首部 然后用户再次发送请求,同时在请求报文中加入一个Authorization首部,然后服务器验证,成功后浏览器会存储这个值,服务器会返回一个200成功状态码(这里注意一下,记录登录账号和密码的是浏览器不是服务器,很多人对这个首部就是存在这个误解),这样有一个问题,就是不同站点的登录账号密码是不一样的,如果你从一个站点浏览到另一个站点的话你可能要用不同的账号密码,甚至有可能你的用户名会被占用,然后你就会选择下网去看奥普拉的脱口秀(雾)

对HTTP的一些理解以及对应用层其他协议的讨论(一)_第2张图片

(上图是用authenticate首部进行登录的过程图解)

  4.胖URL,这是一个比较臃肿的方法,就是用户首次访问某个web站点时会生成一个唯一的ID,然后服务器去识别这个ID,并且将这个ID添加到URL中区,然后服务器就会将客户端重新导向到这个胖URL中去,然后服务器如果收到了一个队胖URL的请求,就会去查找对应的那个ID,然后重写所有的输出连接,这样看起来就好像服务器为一个特定的用户开辟了一个特定的web空间一样,这样做的缺点是很多,比如最直观的就是URL很丑,又长又臭,其次就是你服务器经常地要重写HTML会加大你服务器的负荷(这种识别方法是真的少见)

5.Cookie,也是我们最常用的一种认证方法,也是最好的方法。可以把cookie认为是一个标签,比如你去和一个女生搭讪,女生就会对你的三观和五官产生一个印象标签,下次你再偶遇她和他搭讪时她就会根据这个印象标签决定采取什么行为(stay cold or stay hot)这里可以把印象标签比作cookie

  那么cookie是怎么工作的呢?先上张图

对HTTP的一些理解以及对应用层其他协议的讨论(一)_第3张图片

  图解已经解释的很详细了 ,这里特别提一下cookie是以键值对的形式保存的,还有就是保存Cookie的是浏览器。

  从上面cookie交互过程不难看出其实cookie本质就是让浏览器积累一组服务器特有的信息,然后每次访问服务器的时候都将这些信息提供给服务器,因为是浏览器负责的保存cookie信息,所以cookie机制也被称为客户端侧状态,各种cookie的集合也被称为cookie jar(cookie罐)

 注意一点就是Cookie jar中的每个Cookie每次发送只会发送给设置了cookie的那个特定的服务器,所以你可以想象响应报文每次返回一个Set-cookie首部时后面必须要带上你的域名方便我客户端的后续发送。

  识别讲完了,接下来讲讲认证的问题。

  认证:认证机制就是对于一些私人数据的访问,我们要用一个特定的证明方式证明我就是那个人,通常我们把认证的用户名和密码称为保密证书,认证的方式主要是两种,基本认证和摘要认证,下面逐个讲讲。

  先讲的是基本认证,他基本过程就是:

  1.客户端发送一条没有认证信息的请求

  2.服务器返回一条响应报文,用401表示拒绝,同时在首部附上WWW-Authenticate ,表示对保护区域的描述 (一般是realm的域值表示要保护的空间 这里很重要)

  3.客户端重新发出请求,同时附上一个Authorization首部 用来说明认证用的算法,用户名和密码

  4.如果验证成功 就在实体中返回请求的内容 同时在一个可选的首部Authentication-Info首部返回一些与授权会话相关的附加信息

  上图!对HTTP的一些理解以及对应用层其他协议的讨论(一)_第4张图片

(上图就是基本认证的大致过程)

  在接下来要讲基本认证的细节之前,先了解一下HTTP是怎么为不同的资源使用不同的访问权限的,上面有一个地方特意点了出来,就是第一次响应报文中的WWW-Authenticate中是要声明一个域名的,那个就是客户端请求的受保护的安全域,我们都知道服务器的资源都是以文件形式管理的,而文件在几乎所有的操作系统中都是以树的方式组织的,安全域可以认为就是那棵文件树以某一个节点为根节点的子树,比如我在WWW-Authenticate中的realm值为“family”,就表示我可以访问以family为根目录的所有子目录。

  关于基本认证有一些细节问题:

  1.HTTP基本认证中,客户端收到质询报文(401状态码那个)时,是将用户名和密码用一个冒号连接起来的,然后用一种叫Base-64编码的方式对他进行编码,Base-64原理简单来说就是将一个8位字节序列划分为一些6位的快,用每个6位的快在一个特殊的由64个字符组成的字母表中选择一个字符,这个字母表包含了大部分字幕和数字

 2.客户端和服务器中间的代理服务器也是可以实现基本认证的,认证步骤和上述相同,但首部和状态码有些许差异,比如代理认证使用407表示质询的

  讲完细节再讲一下基本认证的缺陷:

  1.Base-64很容易反编码,所以你传送的基本就是直接把用户名和密码暴露在外的报文,如果被抓包的话很容易泄露账号信息

  2.即时你用一些比Base-64更高级的加密方式加密,只要我把你的报文抓了,就可以一次次重复地发送个服务器,获得权限

 总之:缺点就是很不安全,既然很不安全,那有没有更安全一点的认证方式呢?这就要引出之后要讲的摘要认证了。

   摘要认证有很多的新特性,但是最核心的一点就是针对基本认证而做出的:永远不要明文发送密码!,那我客户端不能直接明文传输密码,我拿什么认证我的身份呢? 很简单:密码的一部分,或者说密码的摘要。

  摘要认证的过程大致可以归纳为这四步

  1.客户端请求一个受保护的文档

  2.服务端收到请求,拒绝提供并发送质询,询问客户端用户名和密码的摘要分别是什么

  3.客户端传递密码的摘要

  4.服务器拿到这个摘要,和他所知道的所以密码进行对比,如果对比成功且对应的是那个用户名,返回文档和成功的状态码

 可以看到:整个过程没有明文发送过密码,那么我们要怎么设置这个密码的摘要呢,以什么算法对信息的主体进行浓缩呢?这就是摘要认证的精髓。

  摘要是通过单向函数实现的,这种单向函数可以把无限的输入值转化为优先的输出值,常见的摘要函数就是我们熟知的MD5,他会将任意长度的字节序列转换为128位的摘要,通常我们写成32个十六进制的字符,同时摘要知识解决了明文截获密码的问题,对于重放攻击(就是抓了你的包再发给服务器获取权限),摘要认证是通过向客户端发送随机数来解决的,这个随机数会经常的发生变化,接下来看看摘要认证的过程:

对HTTP的一些理解以及对应用层其他协议的讨论(一)_第5张图片

  上图就是摘要认证的产生摘要和产生随机数防止重放攻击的过程,可以看到在服务器收到客户端请求时会计算一个随机数,然后发送质询首部时会顺带发送安全域,随机数和对应的算法,而客户端收到质询后会从他的算法集中选择一个算法,用来产生摘要和客户端的随机数,然后吧客户端选择的算法和摘要以及随机数一起发给服务器,服务器对摘要进行验证,同时产生一个被称为rspauth的摘要以及下一个随机数,再通过响应报文发回给客户端,客户端验证rspauth摘要防止重放攻击和服务器伪装

  这只是一些非常粗略的介绍,接下来要将的就是他内部的工作原理,他是怎么对公共信息,保密信息和有时限的随机数这个组合进行单向的函数加密的:

  摘要算法主要有两种:MD5和MD5-SESS,他们都有以下三个组件组成:

  1.单向散列函数H(d) ,摘要KD(s,d)组成的一对函数,s表示密码,d表示数据

  2.一个包含了安全信息的数据块,比如密码等信息的,称为A1

  3.一个包含了请求报文中非保密属性的数据块,称为A2

  不管采取哪种算法,都会用函数H来计算数据的MD5值,用摘要函数KD来计算以冒号连接的密码和非密保数据的MD5,如下

  H()=MD5()

  KD(,)=H(concatenate(:)) (concatente函数表示两个连接起来)  

  这里注意一点就是A1仅仅包含用户名,密码,保护域,随机数等受保护的内容,和报文自身是无关的,他是用来和H、KD、A2一起计算摘要的,在MD5中他由三部分组成:用户名,域,密码,如下

   A1=::

而MD5-sess中他在发送WWW-Authenticate响应收到客户端回送请求时对三者进行了一次MD5加密,并加入随机数和客户端随机数

  A1=MD5(::)::

而A2表示的仅仅是与报文自身相关的信息,和保密信息无关,包括像URL,方法这种,在MD5中,对于内容是由RFC2617规定的保护质量(qop)进行选择的,qop=auth时,包括请求方法,URL,如下:

  A2=:

而qop=auth-int时 会对报文实体进行MD5加密,提供报文完整性的检测:

  A2=::H()

而最后的摘要就是取A1的摘要,随机数以及A2的摘要的冒号连接的摘要值,用式子表示就是:

 KD(H(A1),::::H(A2))

化简之后就是:

 MD5(MD5(A1):::::MD5

  其实摘要认证是一个比较复杂的话题,配合一些报文实例才会更清晰,三言两语讲不清,有时间的话我会单独开一片博客讲讲认证加密的问题。

  目前看来,摘要认证已经是一个非常完善的加密算法了,他防止了密码在web中明文传输,同时通过带时间戳的随机数有效防止了重放攻击,然而他还存在一些缺陷:比如用摘要认证的时候可能会穿过一些代理而用户甚至意识不到,这个时候如果有人恶意入侵这些代理就可能会修改他的认证机制(比如改成最薄弱的基本代理)从而窃听客户端的信息,还有诸如选择明文攻击的风险以及摘要认证是不会为内容的安全提供任何保证,这就引出接下来要讲的一个更加强大的安全协议:HTTPS

  HTTPS和传统HTTP最大的不同的不同就是在应用层之下加个一个安全层,一般是SSL协议或者是TLS协议,我们会不严谨的用SSL去表示SSL和TLS两个协议:

  介绍SSL协议加密前先讲一些关于密码学的基本知识:

  首先是密码,他是和明文对应的,表示一个明文通过编码器之后的结果,还有一个术语叫密钥,表示加密时候的参数,你可以把加密理解为C语言里面一个有返回值的函数,函数传递两个参数:明文和密钥,返回值就是密码,而解码器反过来,输入一个密钥和密文,输出就是明文,我们常常听到的对称密钥加密就表示编码和解码用的是同一个密钥,有了密钥就可以极大地简化加密过程了,通常我们会公开编码和解码的算法,而选择把密钥作为唯一保密的东西,而对称密钥就会带来一个问题就是N个人要两两进行通信的话就要保存N^2个密钥,这是很恐怖的。

  所以我们会采取一个公开密钥加密技术,就是给每台主机设一个公钥和一个私钥,公钥用来对要传送到这台主机的明文进行编码,私钥用来对接收到的密码进行解码,公钥是公开的,所有主机都能看到,而私钥是保密的,只有主机自身可以看到,这种公开密钥加密最大的挑战就是一台主机可能拥有三个信息:公开的密钥,web上传递的部分密文以及通过公钥加密获得的一条报文和他对应的密文,而我们需要一个算法确保一个人拥有这些信息后仍然无法获知其他主机的私钥,而RSA算法可以完美的解决这个问题,关于RSA,就不做深入介绍了(其实是不会......)

  再介绍两个概念:数字签名和数字证书,前者表示信息的发送者产生的别人无法伪造的一段数字串,这段数字串同时也是对信息的发送者发送信息真实性的一个有效证明,后者则是一个经证书授权中心数字签名的包含公开密钥拥有者信息以及公开密钥的文件,类似于驾照和身份证这种标识一个人信息的证书。

  开始讲SSL协议了,SSL协议是一个二进制协议,他用的是和HTTP不同的端口,一般是443(HTTP默认是80),如果我们选用的是HTTPS协议,我们是打开一条到服务器端口443的连接,然后进行握手,以二进制个数和服务器交换一些SSL安全参数附上加密的HTTP命令,他的连接过程如下:

  1.客户端和服务器建立一个TCP连接,端口号一般为443

  2.客户端和服务器进行SSL安全参数握手,这里包括四步:客户端发送包括可供选择的密码并请求服务器证书,服务器发送选中的密码和证书,客户端发送保密信息同时客户端和服务器一起生成公钥,客户端和服务器相互告知这个公钥是什么,之后传递密文时都用这个密文进行加密

  3.客户端发送HTTP请求,HTTP经过SSL加密,在TCP传递的就是已加密的请求

  4.服务器按相同的流程发送已加密的请求

  5.SSL关闭通知

  6.TCP连接关闭

  

你可能感兴趣的:(网络和协议)