HTTP介绍
当TCP/IP逐步流行后,数据传输变得非常容易,任何终端,不管是个人计算机还是手机设备,只要支持TCP/IP,数据就能够从世界的任意一端传输到另外一端,距离不再是问题。但互联网上传输的数据只有计算机才能明白其中的含义,普通用户不理解传输的字节流,为了让接收方理解发送方发送的数据,计算机软件必须翻译这些字节流。为了让通信双方以同样的规则理解字节流,软件设计者必须定义一个标准,通信双方基于同样的标准才能理解数据的含义,这就是应用层HTTP的雏形,通过HTTP开发者不用额外创建通信规则,更有利于信息的传输和交换。
接下来考虑的一个问题是互联网数据类型,即数据代表的语义信息,文字是世界上最悠久的知识传播工具,在web早期,HTTP传输的数据就是简单的文本信息。Web发展到现阶段,数据类型越来越多,比如可以是视频、图片等元素,在HTTP中通过Content-Type头信息表示数据传输类型。
- HTTP,超文本传输协议,超文本就是HTML,传输表示由HTTP负责客户端和服务器的数据传输和解析。客户端发送一个HTTP请求至服务器,服务器响应该请求,将数据再发送给客户端。HTTP由一系列规则组成,客户端和服务器需要正确的处理这些规则,HTTP可以认为是信息的载体,信息的内容是由HTML页面组成的。
- URL,web由很多资源组成,比如HTML页面、视频、图片,在互联网上每个资源都有一个编号,这个编号就是URL地址。服务器负责定义URL,世界上任何一个资源的编号是唯一的,客户端通过URL地址在互联网中找到该资源,URL官方名称叫做统一资源标识符。
- HTML超文本标记语言,客户端通过HTTP接收的资源一般是一个HTML页面,用户并不理解HTML页面,必须有客户端将HTML页面转换为用户能理解的内容,本质上HTML也是一门语言。
URL规则定义如下:
http://www.example.com:80/index.html
http表示资源需要通过HTTP这个协议才能够获取,换句话说,客户端需要通过HTTP这个协议请求这个资源。www.example.com表示服务器地址,在互联网中每个服务器都有一个IP地址,但对于用户来说IP地址很难记住,用户一般只会记住服务器主机名称。在HTTP中,客户端发送HTTP请求的时候,必须通过DNS协议将服务器主机名转换为IP地址,这样客户端才能找到服务器。
80是HTTP协议的默认端口,表示服务器通过80端口提供HTTP服务。
/index.html表示服务器在/根目录下有一个index.html资源。
这就是URL的全部,主要是定义资源的互联网地址,URL虽然和HTTP是紧密关联的,但在Web中是相互独立的。
理解HTTP
HTTP提供了一组规则和标准,从而让信息能够在互联网上进行传播,也正是通过HTTP,互联网上的设备才能够互相通信,并明白对方的含义。
HTTP目前的版本是HTTP/1.1,定义在RFC2016规范上,相比HTTP/0.9和HTTP/1.0版本,HTTP/1.1并没有太多的演变,只是从性能和标准化的维度做了些优化。互联网发展到目前,用户数、终端类型、共享信息越来越多,信息表现形式也越来越丰富,基于HTTP/1.1的web应用已经不能很好地满足需求,下一代的HTTP标准是HTTP/2.0。
HTTP的模型很简单,是一个B/S模型,由客户端和服务器组成,交互流程很简单。
一个HTTP客户端发送请求至HTTP服务器,然后等待服务器的响应。
一个HTTP服务器负责监听端口,然后等待客户端的请求,处理完成后,回复给客户端。
HTTP语义
HTTP消息主要包括两部分,分别是HTTP语义和HTTP实体。HTTP消息由三部分组成。
1、请求行或响应行
2、HTTP头部
3、HTML实体,包括请求实体和响应实体。
前面两部分是HTTP的语义信息,客户端和服务器使用语义信息进行交谈,最后一部分是HTML实体,由浏览器进行处理。
HTTP头部
HTTP头部协助客户端和服务器进行交谈,了解双方想表达的意思。
(1)Accept-Encoding:gzip
表示浏览器支持的数据压缩算法是gzip,它等于告知服务器,是否可以使用gzip算法压缩响应后再发送。服务器收到请求后解析Accept-Encoding头部,了解客户端希望使用gzip压缩算法压缩HTTP响应。如果服务器支持gzip压缩算法,会针对所有的HTML响应压缩后再发送给客户端,为了让客户端知道响应是经过gzip压缩的,需要输出Content-Encoding:gzip头部,如果服务器不支持gzip算法,也可以原样将HTML响应发送给客户端,并且不输出Content-Encoding头部。
(2)Host:www.example.com
该头部只对客户端有用,表示客户端连接互联网上的某个服务器,客户端在连接之前需要先通过DNS协议解析出www.example.com的IP地址,然后连接服务器并发送请求。
在HTTP中,有很多HTTP头部,某些头部只适用于客户端或服务器,某些头部同时适用于客户端和服务器。RFC协议要求客户端和服务器正确地理解头部,但是在现实世界中,很多客户端和终端设备没有严格地遵守HTTP。
请求行
GET /index.html HTTP/1.1
请求行由方法、URL、HTTP版本组成。方法表示客户端以何种方式请求服务器上的资源。URL表示互联网资源的地址;HTTP/1.1表示客户端本次请求所遵循的HTTP版本,服务器也要按照同样版本的HTTP处理语义信息;本例表示请求服务器/index.html资源。
响应行
HTTP/1.1 200 OK
响应行由HTTP版本,状态码,信息提示符组成。
网络模型
TCP/IP有两个最大的特点,分别是分层和拆包/封包机制。TCP/IP对网络进行了抽象,共划分为四层,每一层的特点不同,完成各自的任务。
应用层
如果没有应用层,那么网络中传输的数据没有任何意义,因为人类无法理解数据的含义,而有了应用层,软件就能解释应用层数据的含义。在web应用中,有了HTTP和HTML标准,浏览器才能呈现用户有意义的内容。
传输层
客户端传输层接收到应用层消息后,负责连接服务器,但服务器有很多服务,在传输层中通过端口来区分服务,通过IP地址和端口号才能构建一条传输通道,对于HTTP来说,服务器端口号默认是80,而客户端的端口是随机产生的。
传输层主要有TCP和UDP,TCP能够保证数据正确地到达,一旦出现错误,会有一系列处理机制,比如重发和校验机制,保证数据正确地传输到对端。HTTP构建在TCP上,在连接阶段,TCP使用三次握手机制确保可靠传输。
三次握手过程如下:
1、初始化连接,客户端发送SYN消息,随机值x请求一个新连接。
2、服务器接受SYN消息,发送SYN ACK响应消息。
3、客户端发送ACK消息确认本次连接成功。
UDP不能确保数据正确传达,比如客户端收到数据后,不会向服务器确认本次接收到的数据有多少,所以服务器也无法确认客户端是否正确收到了数据,UDP的有点就是性能高,减少了很多开销。
网络层
网络层主要是IP这个协议,客户端和服务器传输的时候,会经过很多节点,IP就是选择一条最优的路径。每个终端都有一张路由表,路由表负责将数据传输到下一个节点,下一个节点在传输到下下个节点,最终到达目的地址。
链路层
应用层、传输层、网络层都是虚拟的,只有链路层才是实体设备,包括光纤、网卡等设备。基于这些设备,数据才能最终到达终端。
接下来简单描述封包/拆包机制,对于客户端请求来说,传输层接收到应用层消息后,在HTTP数据包前面增加TCP包头,然后发送给网络层;网络层在TCP数据包前面加上IP包头发送给链路层;链路层在IP数据包前面加上以太网包头;最终服务器接收到完整的数据包。
然后服务器进行拆包:首先在网络层去除链路层包头;在传输层去除IP包头;在应用层去除TCP包头;最终得到完整的HTTP应用层数据。
Socket和TCP
为了通信,每个终端设备都必须支持TCP/IP,比如Windows计算机上都安装了TCP/IP驱动程序。
协议安全分析
互联网应用安全包括两部分:
HTTP本身的安全问题。
Web应用安全问题。
协议不安全的根本原因
HTTP设计之初根本就没有考虑安全问题,它的设计目的是数据传输和共享。安全问题主要有三点原因,这三点也是安全领域的根本问题,任何基于TCP/IP的应用都会遇到。
- 数据没有加密:HTTP本身传递的是明文,不会加密这些信息,只要攻击者能够获取这些明文,用户的隐私就完全暴露了。HTTP是基于TCP/IP的,TCP/IP的特点也决定了HTTP数据很容易被截获,网络传输过程中,路由策略决定HTTP数据会通过很多节点设备,节点很轻松就能截获明文数据,由于数据没有加密,很容易理解其含义。HTTP数据确实加密了,但是HTTP头部却没有加密,而头部信息泄露也是安全问题之一。应用语言可以对信息加密,但浏览器接收到消息的时候,并不知道如何解密消息,这种方案至少在Web应用上没有可行性,因为违反了HTTP标准。
- 无法验证身份:虽然TCP能够确保通信双方正确地传输数据,但是在HTTP应用中,客户端和服务器并不能确认对方身份,在HTTP标准中,没有检验对端身份的标准,对于服务器来说,它接收的HTTP请求格式只要正确,就发送响应信息。对于客户端来说同样如此,它连接的是www.example.com主机,但对于客户端来说,它无法校验服务器的身份。由于通信双方无法确认对方,实现HTTP应用非常灵活,也产生了很多中间设备,比如代理服务器、网关服务器,这些中间设备对于丰富和加速HTTP网站有着巨大的作用。比如,很多公司为了加速访问HTTP网站,所有的客户端都可以配置一个缓存代理服务器,代理服务器一般透明转发客户端的请求,代理服务器接收到响应后会缓存一份数据,下一个用户如果访问同样的网址,直接可以从代理服务器缓存中获取到数据,不用访问真实的网站,这就是缓存代理服务器的作用。
- 数据易篡改:HTTP数据在传输过程中,会经过很多节点,这些节点都可以修改原始数据,而对于客户端和服务器来说,没有任何技术来确保接收的数据是发送者发送的原始数据。由于没有机制确保数据的完整性,客户端和服务器只能无条件信任接收到的数据,这也产生了很多安全问题,篡改数据也叫做中间人攻击,比如JSP插入广告的例子,如果有一种机制能够让浏览器知晓数据已经被篡改,那么浏览器就可以告知用户危险,并中断本次请求。
HTTP安全问题主要是这三点导致的,而解决办法就是使用HTTPS。
密码学
解决HTTP安全的方法就是采用HTTPS,理解HTTPS之前必须掌握基本的密码学知识。
密码学的四个目标
- 机密性(隐私性):在网络中传递的数据如果具备机密性,那么传输的数据就是一串无意义的数字,只有拥有密钥的才能解释这些数据,密钥是加密算法的关键。在密码学中,对称加密算法和公开密钥算法能够保证机密性。
- 完整性:完整性表示接受方能够确保接收到的数据就是发送方发送的原始数据,假设数据被中间人篡改,接收方如果有策略知晓数据被篡改了,那么传递的数据就具备完整性。
- 身份验证:互联网应用一般都有发送方和接收方,对于接收方来说,必须确认发送方的身份,才能确保收到的数据就是真实发送方发送的。反之对于发送方来说也是一样的,通信双方必须确保对端就是要通信的对象。在密码学中,一般使用数字签名技术确认身份。
- 不可抵赖性:在密码学中,数字签名技术能够避免抵赖。
OpenSSL
检查OpenSSL版本
openssl version
随机数
随机数的特性
- 效率:在软件或者密码学应用中需要大量的随机数,必须在很短时间内生成随机数,否则就不是一个好的随机数生成器。
- 随机性:生成的随机数只要不存在统计学偏差,那么这个随机数就具备随机性,比如随机数生成器从0到9几个数字中随机选出四个数字,在生成的随机数中,0到9数字出现的次数是平均的,代表该随机数生成器具备随机性。
- 不可预测性:密码学中的随机数必须具备不可预测性,否则就会存在安全问题。
- 不可重现性:所谓不可重现性就是不管多长时间,不会产生完全相同的随机数。在软件层面不可能生成完全不一样的随机数,在一定周期内,密码学随机数算法最终会生成两个完全相同的随机数,只是周期长短的问题。
随机数的工作原理
不管是真正的随机数生成器TRNG,伪随机数生成器PRNG,还是密码学伪随机数生成器CPRNG,内部的工作原理是一样的,CPRNG是PRNG随机数生成器中的一种。
随机数生成器内部会维护一个状态,对于TRNG来说,内部状态的数值来自外部设备,称为熵,比如动态时间、变化的温度、声音的变化、鼠标的位置。
而对于PRNG来说,内部状态的数值来自于模拟的数值,称为种子(seed)。随机数生成器每次生成随机数的时候,内部状态的值都会变化,这样才能产生不一样的随机数,如果每次熵和种子是一样的,生成的随机数也是相同的,所以熵和种子对于随机数生成器非常重要。
一个优秀的随机数生成器就在于寻找尽可能多的熵和种子,一旦熵和种子不够,随机数生成器就会停止运行。
Hash算法
随机数生成器算法和密码学Hash算法都是密码学中的基础算法,很多其他的密码学算法选择这两个算法作为加密基元。
加密基元
加密基元就是一些基础的密码学算法,通过它们才能够构建更多的密码学算法、协议、应用程序。
Hash算法的用途
- 文件比较:某个用户从互联网上下载了两个MP4格式的电影,但不确定是不是同一个电影,最快的比较方式就是计算这两个电影的摘要值。采用Hash算法的原因有两点:不管多大文件,摘要值的计算非常快速;第二个原因就是不同的文件,内容即使99%相同,对应的摘要值也是不同的。
- 身份校验:这也是hash算法比较常用的一种功能,微博和微信系统中每个用户都有一个口令,用户登录微博和微信的时候,系统需要校验口令,校验通过则表示用户具有管理权限。大概的步骤如下:用户输入用户名和口令登录微博或者微信。系统使用Hash算法计算出口令的摘要值。系统使用用户名和摘要值在数据库表中进行检索,一旦匹配到就说明该用户输入的口令是正确的。
什么是安全的密码学Hash算法
密码学Hash算法除了常规的Hash算法的特性,还应该具备下面三个特性:
- 强抗碰撞性:如果两个不相同的值能够得到同样的摘要值,表示产生了Hash碰撞。密码学中,Hash算法必须具备强抗碰撞性,否则不应该使用。
- 弱抗碰撞性:给定一个消息和这个消息对应的摘要值,很难找到一条不同的消息也具有相同的摘要值。如果某个算法不符合该特性,表示该算法遇到了second-preimage攻击。
- 单向性:给定一个摘要值很难找出它的原始消息,如果计算出原始消息,表示该算法遇到了preimage攻击。
密码学Hash算法的分类
- MD5:是一种常用的Hash算法,摘要值长度固定是128比特,MD5算法目前被证明已经不安全了,MD5算法违反了强抗碰撞性,但是还没有破坏单一性原则。
- SHA:SHA算法不是一个算法,而是一组算法,主要分为三类算法:SHA-1算法类似于MD5,输出的长度固定是160比特。SHA-2算法是目前建议使用的Hash算法,截至目前是安全的,主要有四种算法,分别是SHA-256、SHA-512、SHA-224、SHA-384,输出的长度分别是256比特、512比特、224比特、382比特。
对称加密算法
所谓数据加密,就是将一段数据处理成无规则的数据,除非有关键的密钥,否则谁也无法得知无规则数据的真实含义。
在密码学中,用于数据加密的算法主要有两种,分别是对称加密算法和非对称加密算法。
首先介绍对称加密算法的知识。不管是对称加密算法还是非对称加密算法主要用来保证数据的机密性,什么是对称加密算法?一般是通过一个算法和密钥对明文进行处理,得到的不规则字符就是密文。
对称加密算法可以用下列公式简单表述:
密文 = E (明文,算法,密钥)
明文 = D (密文,算法,密钥)
E和D分别表示加密和解密,通过公式可以了解几个关键点:
- 密钥是关键,密钥是一串数字,加密和解密使用同样的一个密钥,如果没有密钥,基于密文是无法获取明文的。
- 加密和解密操作是一个互逆的过程。
对称加密算法有两种类型,分别是块密码算法和流密码算法。密钥长度是对称加密算法中非常关键的一个概念,密钥长度决定了算法的安全性。
流密码算法
一次性密码本的概念,一次性密码本诞生了流密码算法。
一次性密码本非常简单,大概原理如下:
- 明文与同样长度的序列进行XOR运算得到密文
- 密文与加密使用的序列再进行XOR运算就会得到原始明文
一次性密码本的核心操作就是XOR运算,公式如下:
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
也就是两个比特可能是1或者0进行XOR运算,如果比特相同,结果那就是0,或者结果就是1.
一次性密码本的关键在于:
-
密钥每次必须不一样,否则同一个明文和密钥就会获得相同的内容。
-
一次性密钥本是无法破解的,原因在于破解者无法确认破解的明文就是原始明文。
流密码算法的工作原理如下: -
密钥流的长度和密钥长度是一样的。
-
密钥流是一个伪随机数,是不可预测的。
-
生成伪随机数需要一个种子,种子流就是RC4算法的密钥,基于同样一个密钥,加密者和密钥者能够获取相同的密钥流。
块密码算法
块密码算法在运算的时候,不是一次性完成的,每次对固定长度的数据块进行处理,也就是说完成一次加密或者解密可能要经过多次运算,最终得到的密文长度和明文长度是一样的。
数据块的长度就称为分组长度,由于大部分明文的长度远远大于分组长度,所有要经过多次迭代运算才能得到最终的密文或明文,块密码算法有多种迭代模式,迭代模式也可以称为分组模式。
- 块密码算法不是一次运算完成的,块密码算法有多种迭代方式,每次迭代固定长度的数据块,这是需要重点理解的。
- 分组长度和密钥长度并没有必然的联系,对称加密算法的安全性取决于密钥长度。
- 如果明文或者密文的长度除以分组长度不是整数倍,需要对明文进行填充,保证最终处理的数据长度是分组长度的整数块。
块密码算法有多种迭代模式。 - ECB模式:ECB模式是最简单的一种迭代模式,这种迭代模式是存在安全问题的,一般不建议使用。加密过程为,将明文拆分成多个数据块,每个数据块的长度等于分组长度,如果最后一个数据块长度小于分组长度,需要进行填充保证最后一个数据块长度等于分组长度。一次对每个数据块进行迭代得到每个数据块的密文分组,将所有密文分组组合在一起就得到最终的密文,密文长度等同于明文长度。解密过程,将密文拆分成多个数据块,每个数据块的长度等于分组长度。依次对每个数据块进行迭代得到每个数据块的明文分组,最后一个明文分组要去除填充值,最终将明文分组组合在一起就得到最终的明文。ECB模式最大的特点就是每个迭代的过程都是独立的,是可以并行处理的,能够加快运算速度。由于固定的明文和密钥每次运算的结果都是相同的,这会造成很多的安全问题。
- CBC模式:CBC模式是一种常见的迭代模式,解决了ECB模式的安全问题。加密过程为,将密文拆分成多个数据块,每个数据块的长度等于分组长度,如果最后一个数据块的长度小于分组长度,需要进行填充保证最后一个数据块长度等于分组长度。首先处理第一个数据块,生成一个随机的初始化向量IV,初始化向量和第一个数据块进行XOR运算,运算结果经过加密得到第一个密文分组。接着处理后续的数据块,第n个数据块会和前n-1密文分组进行XOR运算,运算结果再进行加密得到第n个密文分组。对于第一个数据块来说,它的前一个密文分组就是初始化向量。将各个密文分组组合在一起就是完整的密文。解密过程为,将密文拆分成多个数据块,每个数据块的长度等于分组长度。对于解密者来说,初始化向量IV是随同密文发送给解密者的,而且该值是不加密的。初始化向量和第一个数据块进行XOR运算,运算的结果经过解密得到第一个明文分组。接着处理后续的数据块,第N个数据块会和前n-1密文分组进行XOR运算,运算的结果再进行解密得到的第n个明文分组,最后一个明文分组要去除填充值。将各个明文分组组合在一起就是最终的明文。
- CTR模式:CTR模式在迭代的时候,相当于是一个流密码的运行模式。每次迭代运算的时候要生成一个密钥流,生成密钥流的方法可以是任意的,但是各个密钥流之间是有关系的,最简单的方式就是密钥流不断递增,所以才叫计数器模式。将密文拆分成多个数据块,和CBC迭代不一样的是不需要进行填充处理。在处理迭代之前,先生成每个密钥流,有n个数据块,就有N个密钥流。根据第n个密钥流可以得到第n+1个密钥流,最简单的方式就是密钥流每次递增增加1。第一个密钥流的获取方式也很简单,就是生成一个随机值(Nonce),Nonce和IV进行XOR运算得到密文分组。迭代运行每个数据块,最终得到明文。解密过程为:将密文拆分成多个数据块,和CBC迭代不一样的是不需要进行填充处理。对于解密者来说,Nonce是加密者随同密文发送给解密者的,而且该值是不加密的。生成每个数据块对应的密钥流,每个密钥流之间是有关系的。迭代加密密钥流和密钥,得到的值和每个密文分组进行XOR运算,得到明文分组。对每个密文分组进行迭代运算,最终得到明文。
CBC模式和CTR模式是常用的两种迭代模式。
消息验证码
在密码学中,很多情况下,传递的消息是没有必要加密,只要确保消息完整且没有被篡改即可。
- 接口数据并不重要,对随机性要求不高。
- 加密和解密过程狠消耗性能。
消息验证码算法的特点:
- 证明消息没有被篡改,这和Hash算法类似。
- 消息是正确的发送者发送的,也就是说消息是经过验证的。
MAC值一般和原始消息一起传输,原始消息可以选择加密,也可以选择不加密,通信双方会以相同的方式生成MAC值,然后进行比较。一旦两个MAC值相同表示MAC验证正确,否则验证失败。
MAC算法的种类
在密码学中,MAC算法有两种形式,分别是CBC-MAC算法和HMAC算法。
CBC-MAC算法从块密码算法的CBC分组模式演变而来,简单地说就是最后一个密文分组的值就是MAC值,在HTTP中应用最多的MAC算法HMAC算法。
HMAC算法使用Hash算法作为加密单元,HMAC结合Hash算法有多种变种,比如HMAC-SHA-1、HMAC-SHA256、HMAC-SHA512。
数字签名
公开密钥算法的另外一种用途就是数字签名技术。
在公开密钥算法中,如果算法用于加密解密,也同样不能防止抵赖,以RSA加密算法举例,由于RSA的公钥是完全公开的,RSA私钥拥有者虽然能够解密,但是并不能确认是哪个客户端发送的消息,同理任何人都可以抵赖。
抵赖出现的根本原因就在于通信双方无法确认对方身份,也就不能进行身份验证。
数字签名的流程
- 签名生成流程:发送者对消息计算摘要值。发送者用私钥对摘要值进行签名得到签名值。发送者将原始消息和签名值一同发给接收者。数字签名技术的本质不是为了加密,所以和签名值一同传递的消息是不用加密的,当然也可以对消息加密后再计算签名值。
- 签名验证流程:接收者接收到消息后,拆分出消息和消息签名值A。接收者使用公钥对消息进行运算得到摘要值B。接收者对摘要值B和签名者A进行比较,如果相同表示签名验证成功,否则就是验证失败。
RSA数字签名算法
RSA算法的用途非常广泛,可以进行数字签名。和RSA加密算法相似,不同的是,RSA加密算法是公钥加密,私钥解密;RSA签名算法是私钥签名,公钥验证签名。
签名的公式如下:
s = m^d (mod n)
解密签名的公式如下:
m = s^e ( mod n)
和RSA加密填充一样,RSA签名算法也有填充机制,分别是RSASSA-PKCS1-v1_5和RSASSA-PSS。对于同样的输入值和密钥对,使用RSASSA-PKCS1-v1_5标准生成的签名值是固定不变的,而对于RSASSA-PSS标准来说,生成的签名值每次都是变化的,所以安全性更好一点。
TLS/SSL协议
TLS/SSL协议包含以下一些关键步骤:
- 传输的数据必须具有机密性和完整性,一般采用对称加密算法和HMAC算法,这两个算法需要一系列的密钥块。
- 所有的加密块都由主密钥生成,主密钥使用密码衍生算法将主密钥转换成多个密码块。
- 主密钥来自预备主密钥,预备主密钥采用同样的密码衍生算法转换为主密钥。预备主密钥采用RSA或者DH算法协商而来,不管采用哪种密钥协商算法,服务器必须有一对密钥,公钥发送给客户端,私钥自己保留,不同的密钥协商算法,服务器密钥对的作用也是不同的。
TLS/SSL协议核心就三大步骤:认证、密钥协商、数据加密,当然还包含很多其他的细节。
加密算法和MAC算法
为了对数据进行加密,可以采用对称加密算法或者公开密钥加密算法,有加密算法必定有MAC算法,两者是一个整体。
公开密钥算法通过两步加密方式也能完成数据加密,所谓两步加密就是客户端和服务端持有一对密钥对,在客户端和服务器端连接的时候分别发送给对方,双方使用对方的公钥加密数据,双方使用自己的私钥进行解密。
使用公开密钥加密算法的缺点就是运算慢,尤其是HTTP传输的数据非常大,所以在大部分Web应用中很少使用公开密钥算法进行加密解密运算。
唯一可行方案就是对称加密算法,比如AES、DES算法。
密钥协商算法
不管采用哪种密钥协商算法,客户端和服务器端最终会协商预备主密钥,预备主密钥转换为主密钥,主密钥最终再转换为密钥块。
预备主密钥有几个特点:
- 每个客户端和服务器端初始化连接的时候生成预备主密钥,每次的值都是不一样的。
- 预备主密钥再会话结束后,会自动释放。
- 预备主密钥必须保证是机密的,确保攻击者无法解密出预备主密钥,也无法猜测出预备主密钥。
在HTTPS中,一般采用RSA或者DH算法协商主密钥,接下来分别进行描述:
- RSA大概流程如下:客户端向服务端发起连接请求,服务器端发送RSA密钥对的公钥给客户端。客户端通过随机数生成器生成一个预备主密钥,用服务器的公钥加密并发送给服务器端。服务器端解密预备主密钥,假如能够正确解密,则说明客户端和服务器端共同协商出一个预备主密钥。
- DH算法大概流程如下:客户端向服务器端发起连接请求。服务器端生成一个RSA密钥对,并将公钥对发送给客户端。服务器端生成DH参数和服务器DH密钥对,用RSA私钥签名DH参数和服务器DH公钥,最后将签名值、DH参数、服务器DH公钥发送给客户端。客户端通过服务器的RSA的公钥验证签名,获取到DH参数和服务器DH公钥。客户端通过DH参数生成客户端的DH密钥对,并将客户端DH公钥发送给服务器端。客户端通过客户端DH私钥和服务器端DH公钥计算出预备主密钥。服务器端接收到客户端的DH公钥,结合服务器的DH私钥计算出预备主密钥。最终客户端和服务器端计算出的预备主密钥能够保持一致。
DH密钥协商算法有两种形式,分别是静态DH算法和临时DH算法。 - 静态DH算法:由于通过DH参数生成DH密钥对需要时间,从效率角度来看,服务器每次发送的DH参数和DH公钥可以是一样的,这种方式不能提供前向安全性。在HTTPS中DH参数和服务器DH公钥是保存在证书中的。
- 临时DH算法:通信双方每次连接的时候,服务器通过DH参数生成的服务器DH密钥对是不一样的,在会话结束后,服务器DH密钥对也会失效,这种方式提供前向安全性。
RSA的前向攻击性:
- 客户端协商算法的关键在于服务器的密钥对,尤其是私钥,因为只有拥有私钥的服务器才能反解出预备主密钥。一旦RSA密钥对的私钥泄露了,就会产生很严重的问题,攻击者的攻击过程如下:客户端和服务器端协商出一个预备主密钥,然后转换出多个密钥块,后续所有数据都会用密钥块加密保护。攻击者由于没有服务器的私钥,所以很难进行攻击,但是攻击者可以截获所有的通信数据并保存下来。过了一段时间,服务器的私钥由于一些原因泄露了,服务器管理员立刻生成新的密钥对,看起来好像影响不大,因为攻击者无法获取到新的私钥。但是攻击者结果了很多历史通信数据,可以通过泄露的私钥计算出历史通信数据的加密密钥块,此时所有历史通信数据将被解密。
这就是前向安全性,由于私钥的泄露导致所有历史通信被解密。 - 静态DH算法:静态DH算法也不能保证前向安全性,具体的攻击过程如下:客户端连接至服务器端,服务器端因为效率的原因,每次发送的DH参数和服务器DH公钥都是相同的。攻击者由于没有客户端和服务器端的DH私钥,所以无法对预备主密钥进行攻击,也就不能解密通信数据,但是攻击者可以截获所有的通信数据并保存下来。客户端的DH密钥对是不会保存的,也就是保存在内存中,一旦协商出预备主密钥就会主动删除,所以攻击者很难攻击客户端的DH私钥。对于静态DH算法来说,服务端的DH参数和DH密钥对会保存在服务器上,一旦DH私钥泄露,攻击者就能够破解出预备主密钥,所有的历史通信数据就会被破解。
对于动态DH算法来说,客户端每次连接的时候,DH密钥对都是重新生成的,即使在某次连接中泄露了,也仅仅会导致本次连接中的加密数据被破解,安全风险相对较小。
中间人攻击
通过RSA或者DH密钥协商算法,服务器需要提供一对密钥,可以是RSA密钥对或者ECDSA密钥对,上面的方案可能会存在中间人攻击。
所谓中间人攻击就是服务器传递给客户端的公钥可能被攻击者替换,这样安全性就荡然无存了。攻击过程如下:
- 客户端向服务器端发起连接请求,期望获取服务器的RSA公钥,攻击者劫持了这个请求。
- 攻击者自己生成了一对RSA密钥对,然后将攻击者的RSA公钥发送给客户端。
- 攻击者然后再向服务器端发送请求,服务器生成RSA密钥对,将RSA公钥发送给客户端,实际上是发送给攻击者。
- 客户端通过攻击者的公钥加密密钥块并发送给服务器,实际上是发送给攻击者。
- 攻击者用自己的RSA私钥解密了密钥块A,然后自己生成一个密钥块B,用服务器的RSA公钥加密后发送给服务器端。
- 服务器端接收到请求后,用自己的RSA私钥解密出攻击者的密钥块B。
- 客户端使用攻击者的密钥块A,采用AES算法加密数据并发送给服务器端,实际上是发送给攻击者。
- 攻击者使用自己的密钥块A,采用AES算法解密出明文,客户端相当于泄露了隐私,攻击者使用密码块B,采用AES算法加密明文后发送给服务端。
- 服务器使用密码块B,采用AES算法加密数据并发送给攻击者。
- 攻击者使用密码块B,采用 AES算法解密出明文数据,此时客户端和服务器端的加密数据被成功破解。
这就是中间人攻击,在TLS/SSL协议中,客户端无法确认服务器端的真实身份,客户端访问某个域名,接收到一个服务器公钥,但是无法确认公钥是不是真正属于某个域名,公钥只是一串数字,需要有一种手段去认证公钥的真正主人,解决方案就是PKI。
公开密钥算法中,所有的网络通信都会存在中间人攻击,这是务必要记住的一点,在HTTPS协议中必须引入了PKI技术解决身份验证的问题,PKI技术的核心就是证书。