对于https大部分人应该都不陌生,相信大家也都知道https是安全的,但是如果问你为什么https是安全的,可能还是会有一大部分人是答不上来的。本人在之前面试和实际项目中也经常碰到https相关的问题,所以最近花了些时间梳理了https的相关知识点,并整理成博客分享出来,希望对一部分人有所帮助。
如果你是手机用户可以点这里查看
想弄懂https为什么安全,我们就需要搞清它的原理,https原理其实并不是很复杂,但是我们需要弄懂它为什么要这样设计。在本篇中我会从一个简单的问题入手,然后以层层递进的方式介绍https,让大家既了解https的原理又能明白它的设计缘由,这样才能帮助我们加深记忆。
HTTPS(Secure Hypertext Transfer Protocol) 中文名叫安全超文本传输协议,它是基于http开发的,可以简单理解为具有数据加密功能的http,其对数据的加解密都是由ssl/tls完成。
SSL(Secure Sockets Layer ),及其继任者TLS(Transport Layer Security)是介于应用层和传输层之间的为网络通信提供加解密的一种安全协议。因为https的安全机制是依赖于ssl/tls实现的,所以要弄懂https主要是要搞清楚ssl/tls的原理。
要理解ssl原理,我们首先需要知道通常情况下是如何对数据进行加密的。
通信双方使用同一个密钥对数据进行加解密,因为只有一个密钥,且该密钥既能加密数据又能解密数据,所以密钥不能公开,应确保只有通信双方知道,要是密钥泄露就会有安全风险。常见的对称加密方式有AES。
通信双方使用两个不同的密钥对数据进行加解密,这两个密钥分别是公钥和私钥,其中公钥加密的数据只有私钥能解,私钥加密的数据也只有公钥能解。私钥不能公开,因为可以根据私钥生成对应的公钥,如果私钥被别人知道这样就没有安全性可言,所以私钥通常会存放在服务端,相反的公钥是可以公开的,公钥通常会分发给客户端。常见的非对称加密方式有RSA。
在现实场景中我们倾向于用对称加密的方式加密数据,原因是非对称加密计算复杂,加密速度慢,性能低下,相比之下对称加密在性能上会有绝对优势。
基于数据加密的原理,使用对称加密通信,双方都需要持有密钥(key),密钥通常是由服务器和客户端其中一方生成然后下发给对方,这个过程我们通常称之为密钥协商。密钥协商有一个关键步骤就是密钥分发,怎么安全地把密钥发送给对方呢?如果密钥是跟随数据一起下发给对方,在分发过程中要是被第三方窃取,那么第三方就能够解密双方的数据往来,这样就有安全风险。
如何安全分发密钥?
密钥分发存在安全问题主要是由于对称加密的特性,即密钥可以解密通信双方的数据,一旦密钥泄露,那么双方加密的数据,第三方都能够破解。基于此考虑于是就有人想出了新的办法,引入非对称加密,一开始服务端先以明文方式下发公钥给客户端,私钥则保留在自己手上,客户端收到公钥后生成对称加密的密钥(本文中我们暂且都叫协商密钥),然后用公钥加密,发给服务端,服务端用私钥解密就能拿到协商密钥了,之后的通信就都基于此密钥加密。
因为公钥加密的数据只有私钥才能破解,所以即使公钥下发过程中被第三方窃取了也没关系,因为客户端生成的协商密钥会用公钥加密,加密后的数据只有私钥能解,第三方用公钥是解不了的,所以协商密钥就不会被泄露,也就保证了密钥分发安全。
总结下,在数据加密传输过程中,基于效率考虑我们通常会用对称加密的方式加密数据,但是因为对称加密需要进行密钥分发,在分发过程中会存在安全问题,所以又引入了非对称加密来保证密钥分发的安全,整个密钥协商的过程采用了非对称加密和对称加密相结合的技术。
使用公钥加密协商密钥的方式貌似解决了密钥分发的问题,但是却引入了新的问题,即公钥分发问题,因为需要事先把公钥发给客户端,在分发过程中是不能保证绝对安全的。
在出现中间人攻击的情况下,公钥分发也是不安全的,主要原因就在于客户端没办法验证服务器的身份,即没办法验证接收到的公钥是一个合法的服务器下发的。
客户端收到公钥后没办法验证该公钥是否由一个合法的服务器下发,所以需要对服务器进行身份认证,类似于给服务器发个“身份证”,只有由一个大家都统一认可的具有绝对权威的机构认证的服务器下发的公钥我们才认为是合法的。
那么由谁来认证呢?这时候就需要引入第三方机构,由第三方来统一对服务器进行身份认证。可以类比现实生活中的身份证,只有在公安局办理的身份证才是合法的,任何个人或组织办理的身份证都是非法,身份验证就是要检验身份证是否是由合法机构办理。
CA机构就类似于派发“身份证”的第三方,它是由大家共同认可的权威机构,你可以理解为公安局或政府机构,只要在它这里进行认证的服务器都会被大家认为是合法,可靠的服务器。CA机构对服务器进行身份认证是通过签发数字证书的方式进行的。
数字证书可以理解为是服务器的身份证,是用于标识服务器身份的数字凭证。其内容可以简单的由下图表示:
数字证书包括签名和明文信息,其详细信息我就不一一描述,感兴趣的同学可以自己去查阅相关资料学习,这里我主要介绍数字证书的几个关键数据。
数字证书可以简单概括为由以下几个部分组成:证书 = 签名 + 公钥 + 其他信息。
数字摘要是将任意长度的消息变成固定长度的短消息,它类似于一个自变量是消息的函数,也就是Hash函数。数字摘要就是采用单向Hash函数将需要加密的明文“摘要”成一串固定长度(128位)的密文这一串密文又称为数字指纹,它有固定的长度,而且不同的明文摘要成密文,其结果总是不同的,而同样的明文其摘要必定一致。除此之外数字摘要还具有不可逆性。常见的用于生成数字摘要的算法有md5,sha1,sha256等。
数字摘要经常被用于进行数据完整性检验。
通常服务器会下发一段明文和其对应的摘要给客户端,客户端拿到数据后对明文也进行同样的hash运算得出对应的摘要,再与服务端下发的摘要进行对比,如果相等就说明数据完整,否则就可以判定数据被篡改。
数字签名是签发者用自己的私钥对一段信息的数字摘要进行加密后的一串数据。数字签名的主要作用是能够用它来验证签名签发者的身份。数据发送方下发一串签名,数据接收方必须用签名签发者私钥对应的公钥才能解密。所以如果数据接收方持有某个对象的公钥,那么它就只能解密该对象签发的签名,这样就能识别签名签发者的身份。
数字证书除了由第三方私钥签名(比如由ca机构签名),其实也可以用自己的私钥签名。
用自己的私钥给自己的公钥签名的证书称为自签名证书。自签名证书可以自我验证,即可以拿证书的公钥解证书的签名。由第三方私钥签名的证书就不是自签名证书,验证时需要由存放第三方公钥的证书验证。
因为证书中的签名是由其签发机构的私钥加密的,所以如果我们知道签发机构的公钥就可以通过公钥是否能解密签名判断证书是否由该机构签发。反过来,假如我们已经知道了一些合法机构的公钥,那么只要用这些公钥去解密证书的签名,通过是否可以解密成功判断证书是否由合法机构签发。
客户端要验证证书合法性首先需要内置合法签发机构的公钥,在通常情况下我们认为只有ca机构签发的证书是合法的,其他对象签发的证书则认为是非法,所以客户端会内置ca机构的公钥,当然不是直接以公钥形式存在客户端,而是以证书形式,即包含ca机构公钥的证书,通常我们称为根证书,也就是客户端会内置ca机构的根证书,主要作用是用于验证服务器证书合法性。
如何让客户端信任自签名证书?
因为客户端默认只会内置ca机构的根证书,那么我们的自签名证书都会认为是非法的,在实际开发中,申请个ca证书往往会比较麻烦而且价格昂贵,所以有些公司会采取用自签名证书来认证服务器,那么怎么让客户端信任我们的自签名证书呢?
如果想让客户端信任我们的自签名证书,那么只需要将自签名证书导入到客户端的合法证书库里就可以,当然不同平台可能会有一些限制,比如Android7以上即使自己导入了证书也是不受信任的,需要通过代码手动开启。在Android中如何信任自签名证书请查看我的另一篇博客,Android信任Https自签名证书详细教程。
如果已经验证证书是由合法机构签发的,那么接下来再通过数字摘要对比进行完整性检验,还有就是验证证书是否过期等其他信息,都检验通过了那么就认为证书是有效的。
下面这张图描述了证书签发的大体流程:
具体流程概括如下:
1.首先服务端需要向ca机构申请证书,将准备好的数据比如公私钥对(只发公钥,私钥自己保留),域名,申请者信息等发给ca机构。
2.ca机构会审核发过来的数据,判断申请者是否是一个合法的个人或组织
3.如果申请通过,那么ca机构就会给申请者签发数字证书,申请者就可以用这个证书作为自己服务器的身份认证。
虽然说ca机构可以给服务器签发数字证书,但实际上,ca机构通常不会直接给每一个申请者签发数字证书,大多数情况下,ca机构只会给一些中间机构签发证书,然后再由这些中间机构去给普通用户签发证书。你可以简单理解为中间机构是ca机构的分支,它们同样具备分发“身份证”的功能,中间机构在一定程度上可以减轻ca机构的压力,并且有了中间机构,ca机构就可以离线存储私钥从而提高安全性。
顶级ca机构的证书称为根证书,中间机构的证书称为中间证书,个人或组织的证书就是最尾端的证书,我们暂且叫服务器证书。由根证书和其签发的下级证书组成的证书集合就是证书链。
下图是在chrome上获取的github网站证书,从中我们看到github证书并不是由顶级ca机构直接签发的而是由一个二级机构签发。
下图描述了证书链上各级证书的关系:
从图中可以看出,服务器证书是由中间机构的私钥签名,中间机构证书又是ca机构的私钥签名的。在验证证书有效性时需要从服务器证书开始,往前追溯验证到根证书是合法的那么才认为该证书合法。
由于大部分客户端只内置ca机构的根证书,所以服务器证书如果是被中间机构签发的,那么在握手过程中需要连同中间机构证书一同下发给客户端,就相当于需要下发一个证书链。
如图是通过抓包工具抓取的一个https请求的证书信息,从中可以看到服务器证书是一个证书链,包括了两个证书。
下图简单描述了服务器和客户端在建立https连接前的一个握手过程:
大概有如下三个步骤:
证书有效性验证如下:
安全性分析,为什么引入了证书机制后就能确保安全?
首先,因为ca根证书是内置在客户端操作系统的,所以基本上不会被第三方窃取,假如证书的明文在下发过程中被篡改了,那么只要客户端检验摘要不一致就能立马发现问题,假如是中间人把签名也一起替换了,因为他没有ca的私钥所以客户端用ca公钥一解密也会立马发现问题。所以引入证书后就能够有效防止中间人攻击,保证公钥分发安全。
到这里我们其实已经把ssl/tls的原理介绍的差不多了,可以概括为主要三点:
下面我们就整理下整个ssl的完整握手过程,见下图:
由图可知ssl/tls握手过程可以概括如下:
相信现在大家应该能够知道为什么https是安全的。因为ssl帮https做了很多事情,比如通过证书机制对服务器进行身份验证防止中间人攻击,通过非对称加密和对称加密结合的方式进行密钥协商来保证密钥分发安全,运用数字摘要的方式进行完整性检验防止数据被篡改等等。因为ssl/tls做了很多安全措施才保证了https数据传输的安全。
最后,再讲下我们经常用的一些抓包工具比如Fiddler是怎么代理https请求的。
理解了https原理后相信再来看这个问题就会变得非常简单。Fiddler代理https的原理可以用下面的图来描述:
主要有如下步骤:
Fiddler做为一个代理软件,它就类似于服务器和客户端之间的一个中间人,通过证书替换的方式完成了一次中间人攻击,所以可以解密和篡改https数据。基于这点,我们可以肯定的是Fiddler证书一定不是正规ca机构签发的,不然不需要安装Fiddler证书我们就可以通过Fiddler抓取到客户端上所有软件的https请求了,所以ca机构肯定不会给Fiddler这种专门做代理抓包的组织签发证书的。