HTTPS 详解
Why HTTPS
近几年来,各大公司都在大力推进 HTTPS 的建设。Google Chrome将非 HTTPS 的网站标注为「不安全」,苹果要求 APP 中需要使用HTTPS进行通信,微信小程序也要求使用HTTPS协议。那么,我们为什么非要做这么一件事呢?
HTTP(Hypertext Transfer Protocol)超文本传输协议,是一种用于分布式、协作式和超媒体信息系统的应用层协议,可以说 HTTP 是当代互联网通信的基础。
但是,HTTP 有着一个致命的缺陷,那就是内容是明文传输的,没有经过任何加密,而这些明文数据会经过WiFi、路由器、运营商、机房等多个物理设备节点,如果在这中间任意一个节点被监听,传输的内容就会完全暴露,,这一攻击手法叫做MITM(Man In The Middle)中间人攻击。
回忆一下几年前遍地都是的运营商劫持,当你访问一个本来很正常的网页,但页面上却莫名其妙出现了一些广告标签、跳转脚本、欺骗性的红包按钮,甚至有时候本来要下载一个文件,最后下下来却变成了另外一个完全不同的东西,这些都是被运营商劫持了HTTP明文数据的现象。
运营商劫持
还有各大公司的员工安全培训里都有一条「不要连陌生的WiFi」,也是类似的原因,恶意WiFi的控制者可以看到和篡改HTTP明文传输的信息。
为了解决HTTP明文传输数据可能导致的安全问题,1994年网景公司提出了HTTPS(HyperText Transfer Protocol Secure)超文本传输安全协议,数据通信仍然是HTTP,但利用SSL/TLS加密数据包。
HTTPS实现原理
前面说到,HTTPS其实就是将HTTP的数据包再通过SSL/TLS加密后传输,那么SSL/TLS又是什么呢?
SSL(Secure Sockets Layer)安全套接层和TLS(Transport Layer Security)传输层安全协议其实是一套东西。
网景公司在1994年提出HTTPS协议时,使用的是SSL进行加密。后来IETF(Internet Engineering Task Force)互联网工程任务组将SSL进一步标准化,于1999年公布第一版TLS协议文件TLS 1.0。目前最新版的TLS协议是TLS 1.3,于2018年公布。
工作流程
我们先来看看HTTPS的加解密流程。
HTTPS加解密流程
用户在浏览器发起HTTPS请求;
HTTPS需要使用一套CA数字证书,证书内会附带一个公钥Pub,而与之对应的私钥Private保留在服务端不公开;
服务端收到请求,返回配置好的包含公钥Pub的证书给客户端;
客户端收到证书,校验合法性,主要包括是否在有效期内、证书的域名与请求的域名是否匹配,上一级证书是否有效(递归判断,直到判断到系统内置或浏览器配置好的根证书),如果不通过,则显示HTTPS警告信息,如果通过则继续;
客户端生成一个用于对称加密的随机Key,并用证书内的公钥Pub进行加密,发送给服务端;
服务端收到随机Key的密文,使用与公钥Pub配对的私钥Private进行解密,得到客户端真正想发送的随机Key;
服务端使用客户端发送过来的随机Key对要传输的HTTP数据进行对称加密,将密文返回客户端;
客户端使用随机Key对称解密密文,得到HTTP数据明文;
后续HTTPS请求使用之前交换好的随机Key进行对称加解密。
对称加密与非对称加密
对称加密是指有一个密钥,用它可以对一段明文加密,加密之后也只能用这个密钥来解密得到明文。如果通信双方都持有密钥,且天知地知你知我知,绝对不会有别的人知道,那么通信安全自然是可以得到保证的(在密钥足够强的情况下)。
然而,在HTTPS的传输场景下,服务端事先并不知道客户端是谁,你也不可能在事先不通过互联网和每一个网站的管理员都悄悄商量好一个通信密钥出来,那么必然存在一个密钥在互联网上传输的过程,如果在传输过程中被别人监听到了,那么后续的所有加密都是无用功。
这时,我们就需要另一种神奇的加密类型,非对称加密。
非对称加密有两个密钥,一个是公钥,另一个是私钥。一般来说,公钥用来加密,这时密文只能用私钥才能解开。
那么,当客户端发起连接请求,服务端将公钥传输过去,客户端利用公钥加密好信息,再将密文发送给服务端,服务端里有私钥可以解密。
但是,当服务端要返回数据,如果用公钥加密,那么客户端并没有私钥用来解密,而如果用私钥加密,客户端虽然有公钥可以解密,但这个公钥之前就在互联网上传输过,很有可能已经有人拿到,并不安全,所以这一过程只用非对称加密是不能满足的。
注意,严格来讲,私钥并不能用来加密,只能用作签名使用,这是由于密码学中生成公钥私钥时对不同变量的数学要求是不同的,因此公钥私钥抵抗攻击的能力也不同,在实际使用中不可互换。签名的功能在HTTPS里也有用到,下文中会说明。
只有一组公钥私钥只能保证单程的加解密,那么如果我们准备两组公钥私钥呢,是不是可以解决这个问题?来看下面这个过程。
服务端有非对称加密的公钥A1,私钥A2;
客户端有非对称加密的公钥B1,私钥B2;
客户端向服务端发起请求,服务端将公钥A1返回给客户端;
浏览器收到公钥A1,将自己保存的公钥B1发送给服务端;
之后浏览器所有向客户端发送的数据,使用公钥B1加密,客户端可以使用私钥B2解密;
客户端所有向服务端发送的数据,使用公钥A1加密,服务端可以使用私钥A2解密。
此时,两条传输方向的数据都经过非对称加密,都能保证安全性,那么为什么不采用这种方案呢?
最主要的原因是非对称加解密耗时要远大于对称加解密,对性能有很大损耗,大家的使用体验很差。
服务端有非对称加密的公钥A1,私钥A2;
客户端发起请求,服务端将公钥A1返回给客户端;
客户端随机生成一个对称加密的密钥K,用公钥A1加密后发送给服务端;
服务端收到密文后用自己的私钥A2解密,得到对称密钥K,此时完成了安全的对称密钥交换,解决了对称加密时密钥传输被人窃取的问题
之后双方通信都使用密钥K进行对称加解密。
看起来是一个非常完美的方案,兼顾了安全性和性能,但是,真的就安全了么?
CA颁发机构
依然考虑中间人攻击的情况,非对称加密的算法都是公开的,所有人都可以自己生成一对公钥私钥。
当服务端向客户端返回公钥A1的时候,中间人将其替换成自己的公钥B1传送给浏览器。
而浏览器此时一无所知,傻乎乎地使用公钥B1加密了密钥K发送出去,又被中间人截获,中间人利用自己的私钥B2解密,得到密钥K,再使用服务端的公钥A1加密传送给服务端,完成了通信链路,而服务端和客户端毫无感知。
HTTPS中间人
出现这一问题的核心原因是客户端无法确认收到的公钥是不是真的是服务端发来的。为了解决这个问题,互联网引入了一个公信机构,这就是CA。
服务端在使用HTTPS前,去经过认证的CA机构申请颁发一份数字证书,数字证书里包含有证书持有者、证书有效期、公钥等信息,服务端将证书发送给客户端,客户端校验证书身份和要访问的网站身份确实一致后再进行后续的加密操作。
但是,如果中间人也聪明一点,只改动了证书中的公钥部分,客户端依然不能确认证书是否被篡改,这时我们就需要一些防伪技术了。
前面说过,非对称加密中一般公钥用来加密,私钥用来解密,虽然私钥加密理论上可行,但由于数学上的设计这么做并不适合,那么私钥就只有解密这个功能了么?
私钥除了解密外的真正用途其实还有一个,就是数字签名,其实就是一种防伪技术,只要有人篡改了证书,那么数字签名必然校验失败。具体过程如下
CA机构拥有自己的一对公钥和私钥
CA机构在颁发证书时对证书明文信息进行哈希
将哈希值用私钥进行加签,得到数字签名
明文数据和数字签名组成证书,传递给客户端。
客户端得到证书,分解成明文部分Text和数字签名Sig1
用CA机构的公钥进行解签,得到Sig2(由于CA机构是一种公信身份,因此在系统或浏览器中会内置CA机构的证书和公钥信息)
用证书里声明的哈希算法对明文Text部分进行哈希得到H
当自己计算得到的哈希值T与解签后的Sig2相等,表示证书可信,没有被篡改
这时,签名是由CA机构的私钥生成的,中间人篡改信息后无法拿到CA机构的私钥,保证了证书可信。
注意,这里有一个比较难以理解的地方,非对称加密的签名过程是,私钥将一段消息进行加签,然后将签名部分和消息本身一起发送给对方,收到消息后对签名部分利用公钥验签,如果验签出来的内容和消息本身一致,表明消息没有被篡改。
在这个过程中,系统或浏览器中内置的CA机构的证书和公钥成为了至关重要的环节,这也是CA机构公信身份的证明,如果系统或浏览器中没有这个CA机构,那么客户端可以不接受服务端传回的证书,显示HTTPS警告。
实际上CA机构的证书是一条信任链,A信任B,B信任C,以掘金的证书为例,掘金向RapidSSL申请一张证书,而RapidSSL的CA身份是由DigiCert Global根CA认证的,构成了一条信任链。
各级CA机构的私钥是绝对的私密信息,一旦CA机构的私钥泄露,其公信力就会一败涂地。之前就有过几次CA机构私钥泄露,引发信任危机,各大系统和浏览器只能纷纷吊销内置的对应CA的根证书。
有些老旧的网站会要求使用前下载安装他自己的根证书,这就是这个网站使用的证书并不能在系统内置的CA机构和根证书之间形成一条信任链,需要自己安装根证书来构成信任链,这里的风险就要使用者自己承担了。
总结
HTTPS 的出发点是解决HTTP明文传输时信息被篡改和监听的问题。
为了兼顾性能和安全性,使用了非对称加密+对称加密的方案。
为了保证公钥传输中不被篡改,又使用了非对称加密的数字签名功能,借助CA机构和系统根证书的机制保证了HTTPS证书的公信力。
评论