type
status
date
slug
summary
tags
category
icon
password
Sub-item
Last edited time
Oct 15, 2023 08:34 AM
Parent item
领域
HTTP的安全问题
http通信需要满足下面的需求:
如果采用普通发HTTP协议通信,所有信息明文传播,带来了三大风险。
(1) 窃听风险(eavesdropping):黑客可以获知通信内容。
(2) 篡改风险(tampering):黑客可以修改通信内容。
(3) 冒充风险(pretending):黑客可以冒充他人身份参与通信。
另外还有黑客故意搞破坏的攻击:
- 重放攻击。双方的通信过程中,“黑客”可以截获发送的加密了的内容,虽然他无法解密这个内容,但是他可以捣乱,例如把信息原封不动的发送多次,扰乱通信过程。
- 破坏加密消息完整性。双方的通信过程中,“黑客”还可以修改截获后的密文修改后再发送,因为修改的是密文,虽然不能完全控制消息解密后的内容,但是仍然会破坏解密后的密文。因此发送过程如果黑客对密文进行了修改,“客户”和“服务器”是无法判断密文是否被修改的。
HTTP安全问题如何解决
- 防止消息被窃听:对数据进行加密。
- 首先服务端生成一个非对称密钥:公钥和私钥。将公钥发给客户端,自己保留下了私钥,客户端用服务端的公钥对消息加密后,再发给服务端,服务端用私钥解密就可以看到消息原文了。在传输过程中的使用的是密文,别人无法偷窥。
- 但在实际中,如果使用非对称加密算法加密数据,**公钥加密计算量太大,如何减少耗用的时间?**解决办法是:每一次对话(session)中,客户端生成一个对称秘钥:"对话密钥"(session key),使用服务端的公钥加密传输给服务端,然后客户端使用"对话密钥"对数据进行加密,服务端使用相同的"对话密钥"钥进行解密,这样就减少了加密运算的消耗时间。
- 防止消息被篡改和证明消息发送者的身份:使用消息认证码。
Bob 看完信后,决定给 Susan 回一封信。为了防止信的内容被篡改,或者别人伪装成他的身份跟 Susan 通信,他决定先对信的内容用 Hash 算法做一次处理,得到一个字符串哈希值,Bob 又用自己的私钥对哈希值做了一次加密得到一个签名,然后把签名和信(明文的)一起发送给 Susan;
- 防止服务器被伪装:服务器公钥必须被安全地送达对方,客户端必须能够判断收到的公钥是不是服务端的公钥。
- 防止重放攻击。可以给通信的内容加上一个序号或者一个随机的值,同时进行签名,如果“客户”或者“服务器”接收到的信息中有之前出现过的序号或者随机值,那么说明有人在通信过程中重发信息内容进行捣乱,双方会立刻停止通信。因为攻击者无法修改该值,否则签名无法验证通过。
- 检查加密消息的完整性。同样使用消息认证码。
解决办法可以概述为:
(1) 客户端向服务器端索要并验证公钥。
(2) 双方协商生成"对话密钥"。
(3) 双方采用"对话密钥"进行加密通信。
公钥的安全传输
他想到了去第三方权威机构"证书中心"(certificate authority,简称 CA)做认证。证书中心用自己的私钥对 Bob 的公钥和其它信息做了一次加密。这样 Bob 通过网络将数字证书传递给他的小伙伴后,小伙伴们先用 CA 给的公钥解密证书,这样就可以安全获取 Bob 的公钥了。数字证书是服务器主动去权威机构申请的,证书中包含了上一个图中的加密过的A公钥和权威机构的信息,所以服务器只需要给客户端下发数字证书即可。现在流程图如下:
数字证书中的A公钥是如何加密的呢?
答案是非对称加密,只不过这里是使用只有权威机构自己才有的私钥加密。
等一下,既然A公钥被权威机构的私钥加密了,那客户端收到证书之后怎么解密证书中的A公钥呢?需要有权威机构的公钥才能解密啊!那这个权威机构的公钥又是怎么安全地传输给客户端的呢?感觉进入了鸡生蛋,蛋生鸡的悖论了~~
答案是权威机构的公钥不需要传输,因为权威机构会和主流的浏览器或操作系统合作,将他们的公钥内置在浏览器或操作系统环境中。客户端收到证书之后,只需要从证书中找到权威机构的信息,并从本地环境中找到权威机构的公钥,就能正确解密A公钥。
这样就绝对安全了吗?既然权威技能能给服务器签发数字证书,那为什么就不可能给中间人签发数字证书呢?毕竟赚钱的生意权威机构也不会拒绝的呀。
试想一下:
服务器给客户端下发数字证书时证书被中间人劫持了,也就是说,中间人将服务器的证书替换成自己的证书下发给客户端,客户端收到之后能够通过权威机构的公钥解密证书内容(因为中间人的证书也是权威机构私钥加密的),从而获取公钥,但是,这里的公钥并不是服务器原本的A公钥,而是中间人自己证书中的B公钥。从第二层可知,如果不能保证客户端收到的公钥是服务器下发的,那整个通信数据的安全就没法保证。简单总结就是证书被调包。
所以,还得保证客户端收到的证书就是服务器下发的证书,没有被中间人篡改过。
这里就有两个需求:
- 证明证书内容没有被第三方篡改过;
- 证明证书是服务器下发的;
其实这些问题,数字证书本身已经提供方案了,数字证书中包含了:
- 加密之后的服务器公钥;
- 权威机构的信息;
- 服务端对证书内容的签名(先通过Hash函数计算得到证书数字摘要,然后用服务端私钥对数字摘要进行数字签名);
- 服务端的签名计算方法;
- 证书对应的域名。
这样一来,客户端收到证书之后:
对于第一个需求:使用权威机构的公钥验证数字证书的签名,确保证书内容(服务器的公钥)以及证书的服务端数字签名没有被篡改,然后根据证书上描述的计算证书签名的方法和原文进行签名验证,验证通过表示证书一定是服务器下发的,没有被中间人篡改过。因为中间人虽然有权威机构的公钥,能够解析证书内容并篡改,但是篡改完成之后中间人需要将证书重新签名,但是中间人没有服务端的私钥,强行乱修改证书,就会导致证书内容和证书签名不匹配。所以证书签名就能判断证书是否被篡改。
对于第二个需求,再考虑证书被掉包的情况:中间人同样可以向权威机构申请一份证书,然后在服务器给客户端下发证书的时候劫持原证书,将自己的假证书下发给客户端,客户端收到之后依然能够使用权威机构的公钥解密证书,并且证书签名也没问题。但是这个时候客户端还需要检查证书中的域名和当前访问的域名是否一致。如果不一致,会发出警告!
从上面的分析可以看到,数字证书中的信息确实能让客户端辨别证书的真伪。
HTTPS完整通信过程
完整的数据加密通信的过程如下:
HTTPS的通信的特性有:
- 连接是安全的
- 连接是可靠的
发送的每条消息都会通过消息验证码(Message authentication code, MAC),来进行消息完整性检查。
- 可以使用公钥对通信双方进行身份验证
该项一般只需要验证一方的身份(通常是服务端)。对于一些非常保密的应用,还是需要验证双方的身份。例如,金融机构往往只允许认证后的用户连入自己的网络。
TLS协议
完成证书的传输并建立安全的传输通道的工作是由TLS协议完成的。每个连接使用不同的对称秘钥。
TLS(Transport Layer Security)是为网络通信提供安全及数据完整性的一种安全协议,TLS继承自SSL(Secure Sockets Layer安全套接字协议)。TLS与SSL在传输层与应用层之间对网络连接进行加密。
SSL是Netscape开发的专门用户保护Web通讯的,目前版本为3.0。最新版本的TLS(Transport Layer Security,传输层安全协议)是IETF(Internet Engineering Task Force,Internet工程任务组)制定的一种新的协议。目前只用HTTP+TLS。
SSL协议可分为两层:
- SSL记录协议(SSL Record Protocol):它建立在可靠的传输协议(如TCP)之上,为高层协议提供数据封装、压缩、加密等基本功能的支持。
- SSL握手协议(SSL Handshake Protocol):它建立在SSL记录协议之上,用于在实际的数据传输开始前,通讯双方进行身份认证、协商加密算法、交换加密密钥等。
TLS握手协议
下图是带证书的双向身份验证的 TLS 握手。
详细过程如下:
- 第一次握手
- 客户端发送 TLS 握手请求。发送 ClientHello 消息,其中包含支持的最高 TLS 协议版本、当前时间、随机数(稍后用于生成“会话对称密钥”)、加密算法列表(如 RSA 公钥加密)和压缩算法列表。
- 服务端回应请求。回应 ServerHello 消息,其中包含要使用的 TLS 协议版本、当前时间、**随机数(稍后用于生成“会话对称密钥”)、要使用的加密算法和要使用的压缩算法**。
如果客户端正在尝试恢复握手,则ClientHello可以包含发送会话 ID。 如果客户端可以使用应用协议协商,则它可能包括受支持的应用程序协议列表,例如 HTTP 2.0。
为了确认恢复握手,则服务端ServerHello可以包含发送会话 ID。 要使用的协议版本应该是客户端和服务端都支持的最高版本。例如,客户端支持 TLS 1.1,服务端支持 TLS 1.2,则应该选择 TLS 1.1 版本。
- 第二次握手
- 服务端发送其证书(可选,取决于使用的加密算法)
- 服务端发送 ServerKeyExchange 消息(可选)
- 根据具体使用的加密算法,可能需要发送一些额外的加密参数,那么就可以通过该ServerKeyExchange消息来发送。
- 如果选择的是RSA协议,那么传递的就是RSA构建公钥密码的参数(E,N)。我们回想一下RSA中构建公钥的公式:
- 如果选择的是Diff-Hellman密钥交换协议,DHE 和 DH_anon 算法会发送该消息,那么传递的就是密钥交换的参数,具体内容可以参考 ECIES:秘钥协商。
- 服务端发送 CertificateRequest 消息,请求获取客户端证书,以便进行相互认证(可选,如果是单向身份认证,通常是服务端认证,则不需要这一步)。比如fabric中,服务器端也需要向客户端索要证书。
- 服务端发送 ServerHelloDone 消息,表示**服务端握手协商完成**(注意,是协商完成,而不是整个 TLS 握手完成)。
- 客户端校验服务端证书(如果证书不合法,客户端会向用户发出警告信息并断开 TLS 握手)。
目前主流的 RSA 算法就是基于证书的。使用证书也是更推荐的做法,因为相比于无证书的机制,使用证书更安全。因此都会发送证书。
只要知道了E和N,那么就知道了RSA的公钥,这里传递的就是E,N两个数字。具体内容可以参考 非对称秘钥。
- 第三次握手
- 客户端发送其证书,供服务端使用和校验(可选,如果是单向身份认证,通常是服务端认证,则不需要这一步)
- 客户端发送 ClientKeyExchange 消息(可选,同样取决于使用的加密算法)
- 如果是公钥或者RSA模式情况下,客户端将根据客户端生成的随机数和服务器端生成的随机数,生成预备主密码,**Pre-Master-Secret (PMS,预主密钥) **是一个随机数,使用服务端证书中的公钥对其进行加密,返送给服务器端。
- 如果使用的是Diff-Hellman密钥交换协议,DHE 和 DH_anon 算法会用到,则客户端会发送自己这一方要生成Diff-Hellman密钥而需要公开的值。这样服务器端可以根据这个公开值计算出预备主密码。
- 客户端发送一个 CertificateVerify 消息(可选,如果是单向身份认证,通常是服务端认证,则不需要这一步),其中包含使用客户端私钥对之前握手信息的签名。服务端可以使用客户端公钥来验证此签名,以确定客户端是否拥有此证书。客户端向服务器端证明自己是客户端证书的持有者。
- 到此为止,客户端和服务端都具有了三个随机数(两个随机数 + PMS),然后它们分别使用之前协商的对称加密算法和三个随机数来生成 Master-Secret(MS,主密钥,也称为“会话密钥”),用于加密之后传输的数据。主密码主要用来生成 1:对称密码的密钥、2:消息认证码的密钥、3:对称密码的CBC模式所使用的初始化向量。
- 第四次挥手
- 客户端发送 ChangeCipherSpec 记录,用于告诉服务端:后面的所有数据都将进行身份验证(如果服务端证书中存在加密参数,则会进行加密),表示后面的消息将会以前面协商过的密钥进行加密。
- 客户端发送经过身份验证和加密的 Finished 消息,其包含之前所有握手信息的 Hash 和 MAC
- 服务端尝试解密 Finished 消息,获取并验证 Hash 和 MAC。如果解密或验证失败,则认为握手失败,断开 TLS 链接。
- 服务端回应 ChangeCipherSpec 记录,同样用于告诉客户端:之后的所有数据都将进行身份验证(如果服务端证书中存在加密参数,则会进行加密)。
- 服务端发送经过身份验证和加密的 Finished 消息,其包含之前所有握手信息的 Hash 和 MAC
- 客户端执行与服务端上一步相同的解密和验证过程
简单可以概括为:
- 第一步,爱丽丝给出协议版本号、一个客户端生成的随机数(Client random),以及客户端支持的加密方法。
- 第二步,鲍勃确认双方使用的加密方法,并给出数字证书、以及一个服务器生成的随机数(Server random)。
- 第三步,爱丽丝确认数字证书有效,然后生成一个新的随机数(Premaster secret),并使用数字证书中的公钥,加密这个随机数,发给鲍勃。
- 第四步,鲍勃使用自己的私钥,获取爱丽丝发来的随机数(即Premaster secret)。
- 第五步,爱丽丝和鲍勃根据约定的加密方法,使用前面的三个随机数,生成"对话密钥"(session key),用来加密接下来的整个对话过程。
握手阶段有三点需要注意。
(1)生成对话密钥一共需要三个随机数。 (2)握手之后的对话使用"对话密钥"加密(对称加密),服务器的公钥和私钥只用于加密和解密"对话密钥"(非对称加密),无其他作用。 (3)服务器公钥放在服务器的数字证书之中。
秘钥可以单独管理,保障私钥的安全性。
某些客户(比如银行)想要使用外部CDN,加快自家网站的访问速度,但是出于安全考虑,不能把私钥交给CDN服务商。这时,完全可以把私钥留在自家服务器,只用来解密对话密钥,其他步骤都让CDN服务商去完成。
整个握手阶段都不加密(也没法加密),都是明文的。因此,如果有人窃听通信,他可以知道双方选择的加密方法,以及三个随机数中的两个。整个通话的安全,只取决于第三个随机数(Premaster secret)能不能被破解。
下面介绍基于DH的不需要证书的TLS 握手。注意:实际中很少使用。
虽然理论上,只要服务器的公钥足够长(比如2048位),那么Premaster secret可以保证不被破解。但是为了足够安全,我们可以考虑把握手阶段的算法从默认的RSA算法,改为 Diffie-Hellman算法(简称DH算法)。
采用DH算法后,Premaster secret不需要传递,双方只要交换各自的参数,就可以算出这个随机数。
session恢复
握手阶段用来建立SSL连接。如果出于某种原因,对话中断,就需要重新握手。
这时有两种方法可以恢复原来的session:一种叫做session ID,另一种叫做session ticket。
session ID的思想很简单,就是每一次对话都有一个编号(session ID)。如果对话中断,下次重连的时候,只要客户端给出这个编号,且服务器有这个编号的记录,双方就可以重新使用已有的"对话密钥",而不必重新生成一把。
图中,客户端给出session ID,服务器确认该编号存在,双方就不再进行握手阶段剩余的步骤,而直接用已有的对话密钥进行加密通信。
session ID是目前所有浏览器都支持的方法,但是它的缺点在于session ID往往只保留在一台服务器上。所以,如果客户端的请求发到另一台服务器,就无法恢复对话。session ticket就是为了解决这个问题而诞生的,目前只有Firefox和Chrome浏览器支持。
上图中,客户端不再发送session ID,而是发送一个服务器在上一次对话中发送过来的session ticket。这个session ticket是加密的,只有服务器才能解密,其中包括本次对话的主要信息,比如对话密钥和加密方法。当服务器收到session ticket以后,解密后就不必重新生成对话密钥了。
TLS记录协议
等握手协议完成之后,接下来就使用记录协议来传输加密数据了,TLS记录协议主要负责消息的压缩,加密及数据的认证:
消息首先将会被分段,然后压缩,再计算其消息验证码,然后使用对称密码进行加密,加密使用的是CBC模式,CBC模式的初始向量是通过主密码来生成的。
Record 协议 – 从应用层接受数据,并且做:
- 分片,逆向是重组
- 生成序列号,为每个数据块生成唯一编号,防止被重放或被重排序
- 压缩,可选步骤,使用握手协议协商出的压缩算法做压缩
- 加密,使用握手协议协商出来的key做加密/解密
- 算HMAC,对数据计算HMAC,并且验证收到的数据包的HMAC正确性
- 发给tcp/ip,把数据发送给 TCP/IP 做传输(或其它ipc机制)。
得到密文之后会附加类型,版本和长度等其他信息,最终组成最后的报文数据。
TLS协议的架构
TLS主要分为两层,底层的是TLS记录协议,主要负责使用对称密码对消息进行加密。
上层的是TLS握手协议,主要分为握手协议,密码规格变更协议和应用数据协议4个部分。
- 握手协议负责在客户端和服务器端商定密码算法和共享密钥,包括证书认证,是4个协议中最最复杂的部分。
- 密码规格变更协议负责向通信对象传达变更密码方式的信号
- 警告协议负责在发生错误的时候将错误传达给对方
- 应用数据协议负责将TLS承载的应用数据传达给通信对象的协议。
握手协议
握手协议是TLS协议中非常重要的协议,通过客户端和服务器端的交互,和共享一些必要信息,从而生成共享密钥和交互证书。
不说话,先上图:
接下来我们一步步的介绍每一步的含义:
- client hello
- 可用版本号
- 当前时间
- 客户端随机数
- 会话ID
- 可用的密码套件清单
- 可用的压缩方式清单
客户端向服务器端发送一个client hello的消息,包含下面内容:
我们之前提到了TLS其实是一套加密框架,其中的有些组件其实是可以替换的,这里可用版本号,可用的密码套件清单,可用的压缩方式清单就是向服务器询问对方支持哪些服务。
客户端随机数是一个由客户端生成的随机数,用来生成对称密钥。
- server hello
- 使用的版本号
- 当前时间
- 服务器随机数
- 会话ID
- 使用的密码套件
- 使用的压缩方式
服务器端收到client hello消息后,会向客户端返回一个server hello消息,包含如下内容:
使用的版本号,使用的密码套件,使用的压缩方式是对步骤1的回答。
服务器随机数是一个由服务器端生成的随机数,用来生成对称密钥。
- 可选步骤: certificate
服务器端发送自己的证书清单,因为证书可能是层级结构的,所以处理服务器自己的证书之外,还需要发送为服务器签名的证书。
客户端将会对服务器端的证书进行验证。如果是以匿名的方式通信则不需要证书。
- 可选步骤: ServerKeyExchange
- 如果选择的是RSA协议,那么传递的就是RSA构建公钥密码的参数(E,N)。我们回想一下RSA中构建公钥的公式:
- 如果选择的是Diff-Hellman密钥交换协议,那么传递的就是密钥交换的参数,具体内容可以参考更加安全的密钥生成方法Diffie-Hellman
如果第三步的证书信息不足,则可以发送ServerKeyExchange用来构建加密通道。
ServerKeyExchange的内容可能包含两种形式:
只要知道了E和N,那么就知道了RSA的公钥,这里传递的就是E,N两个数字。具体内容可以参考RSA算法详解
- 可选步骤: CertificateRequest
如果是在一个受限访问的环境,比如fabric中,服务器端也需要向客户端索要证书。
如果并不需要客户端认证,则不需要此步骤。
- server hello done 服务器端发送server hello done的消息告诉客户端自己的消息结束了。
- 可选步骤:Certificate
对步骤5的回应,客户端发送客户端证书给服务器
- ClientKeyExchange
- 如果是公钥或者RSA模式情况下,客户端将根据客户端生成的随机数和服务器端生成的随机数,生成预备主密码,通过该公钥进行加密,返送给服务器端。
- 如果使用的是Diff-Hellman密钥交换协议,则客户端会发送自己这一方要生成Diff-Hellman密钥而需要公开的值。具体内容可以参考更加安全的密钥生成方法Diffie-Hellman,这样服务器端可以根据这个公开值计算出预备主密码。
还是分两种情况:
- 可选步骤:CertificateVerify
客户端向服务器端证明自己是客户端证书的持有者。
- ChangeCipherSpec(准备切换密码)
ChangeCipherSpec是密码规格变更协议的消息,表示后面的消息将会以前面协商过的密钥进行加密。
- finished(握手协议结束)
客户端告诉服务器端握手协议结束了。
- ChangeCipherSpec(准备切换密码)
服务器端告诉客户端自己要切换密码了。
- finished(握手协议结束)
服务器端告诉客户端,握手协议结束了。
- 切换到应用数据协议
这之后服务器和客户端就是以加密的方式进行沟通了。
主密码和预备主密码
上面的步骤8生成了预备主密码。主密码是根据密码套件中定义的单向散列函数,实现了伪随机数生成器+预备主密码+客户端随机数+服务器端随机数生成的。
主密码主要用来生成 1:对称密码的密钥、2:消息认证码的密钥、3:对称密码的CBC模式所使用的初始化向量。详见分组密码和模式
TLS记录协议
TLS记录协议主要负责消息的压缩,加密及数据的认证:
消息首先将会被分段,然后压缩,再计算其消息验证码,然后使用对称密码进行加密,加密使用的是CBC模式,CBC模式的初始向量是通过主密码来生成的。
得到密文之后会附加类型,版本和长度等其他信息,最终组成最后的报文数据。