HTTPS为什么是安全的

163 阅读9分钟

HTTPS是什么?

HTTPS并不是一个独立的协议,而是对HTTP的加强和补充,其中后缀S意思为Secure,安全。

简单来说,HTTPS = HTTP + TLS(SSL已经被弃用)。

所以HTTPS是一个组合概念,其保证安全的部分由TLS来实现。

TLS

TLS: 传输层安全性协议(英语:Transport Layer Security,缩写:TLS

从互联网OSI模型来说,TLS和HTTP同属于应用层的协议。但是从实际使用,TLS位于TCP/UDP之上,HTTP之下,与HTTP协议本身并无耦合。

TLS的版本

目前最新的版本是TLSv1.3版本,于2018年8月发表。

TLS协议的前身是另一个大家很熟悉的协议SSL,但SSL因为安全问题(3.0版本发布于1996年),在2014年被Google(Chrome),Mozilla(Firefox)相继禁用。

为什么能保证安全?

经过以上的介绍,回到标题,我们知道了,HTTPS是由TLS协议来保障安全的,那么TLS是怎么保证通讯安全的呢?

TLS的几个步骤

  1. 身份验证: 服务器将证书(由CA机构签名)发给客户端,客户端验证证书的有效性。

  2. 密钥交换: 经过验证之后,客户端和服务器协商对称加密密钥。

  3. 加密通讯: 经过密钥协商之后,客户端和服务器通过密钥进行加密的通讯数据。

为什么要有这三个步骤?从HTTP说起

首先,我们要了解,互联网中每台设备,都可以截获或修改经过此设备的所有数据包,TCP协议本身是不加密的,而HTTP也是明文的,这意味着一条HTTP请求,对于所经过的每一个网络设备,设备owner只要有心,都可以监听并任意修改。

要想对TCP/IP协议进行安全改造,显然不太合适,整个互联网的软硬件基础设施都是基于TCP/IP协议的,改造成本太高了。所以HTTPS选择加密HTTP协议所传输的内容,包括Header和Body,然后仍然基于不安全的TCP协议进行传输。

对称式加密

设想一个HTTP服务场景,有Server和Client双方,有且仅有C、S双方知道密钥K。

Client现在打算发送Request请求给Server,Client使用密钥K将Request Body加密,得到K(Body),然后将K(Body)通过Tcp协议传输给Server。

这样即使网络传输的中间环节有人(某台网络设备的owner) B可以截获信息,但是信息本身是加密的,中间人B无法读懂K(Body),中间人可以修改加密后的数据K(Body),但是他不知道加密规则也就是密钥K,所以也无法修改Body原文。

以上环节的重点在于,C/S双方需要先协商生成一个仅有双方知道的密钥K,然后才能在网络上加密安全通讯。那在双方确认和交换密钥K时,通讯仍然是未加密不安全的,此时如果K被中间人截获,那么之后的安全通讯就无从谈起了。

所以问题的关键是解决如何在不安全的网络环境下,安全地交换密钥K。这也是对称式加密的困局,有K才能加密,但是K本身却仍然只能在不安全的环境中传递。

此时,非对称式加密或者说公开密钥加密算法就闪亮登场了。

非对称式加密——RSA

区别于对称式加密,只有一个密钥K,非对称式加密使用了公钥和私钥一对密钥来进行加密通讯。

最广泛使用的非对称式加密算法是RSA算法,对于一对生成的公钥和私钥,他们有如下特性:

  1. 公钥和私钥都可以加密数据
  2. 公钥加密之后,只能由私钥来解密,自身不能解密
  3. 私钥加密之后,可以由公钥来解密

上述特性是如何能解决在不安全的环境中安全地交换密钥K的呢?

  1. 服务器Server生成一对公钥和私钥,并通过不安全的网络,将公钥发送给Client。
  2. Client生成密钥K,并使用公钥将密钥K加密,传递给Server。
  3. Server收到Client的密文,通过私钥解密,获得密钥K。
  4. 之后,Server和Client之间将使用密钥K通讯,至此安全的传输通道建立。

假设,中间人在第1步通过监听获得了Server的公钥,但公钥无法解密密文,所以在第2步Client发往Server的K,中间人无法解密。只有Server能通过私钥解密获得K。

身份认证

上面的逻辑其实并没有完美解决中间人攻击的问题。

中间人B很聪明,自己也生成了一组RSA公钥和私钥,分别是公钥B,私钥B。

在上面的通讯流程第1步中,中间人截获到Server发给Client的公钥S,替换成自己的公钥B发送给Client,那么Client在第二步中,使用了公钥B加密K,发送给Server,此时中间人截获此信息,通过自己的私钥B解密就获取到了密钥K。 之后中间人就可以使用公钥S重新加密K,发送给Server,那么K就在Client和Server之间完成了交换,但是中间人也已经获知了K,可以随意监听篡改Client和Server的通讯。

所以,为了杜绝中间人攻击,Client需要能验证自己收到的公钥确实是Server发来的,即当Client收到公钥B时,能确认公钥B不是Server的公钥。

所以就有了HTTPS证书的概念,权威机构的证书,其实就是对公钥S的认证。

把例子具体一点。假设Server就是example.com,Client是一个普通用户。现在,用户收到了example.com发来的公钥S,怎么证明S就是来自example.com,而不是中间人自己的公钥呢?

证书

其实在实际HTTPS应用中,用户访问example.com时,收到的不只是example.com的公钥S,而是一份由权威机构颁发的证书,证书上面写着:

  • 域名:example.com
  • 公钥:公钥S

如果中间人篡改了证书,把公钥字段换成自己的公钥B呢?或者说干脆伪造一个证书,如何识别?

这就要再提到证书必不可少的签名了。

签名

中间人B是无法篡改上面那份证书的,因为证书有权威机构的签名,如果中间人B把证书中的公钥改成自己的公钥B,那么证书就无法和签名对应上了。

签名实际也是对非对称式加密算法的应用。权威机构使用自己的私钥给网站证书做加密,而用户计算机里拥有权威机构的公钥,于是可以解密证书,与实际获得的证书对比验证是否一致。

而中间人没有权威机构的私钥,无法给伪造的证书签名,或者说,中间人如果自己给证书签名,那么这个签名是得不到用户认可的,因为用户PC只会内置一批受信任的根证书(公钥)。

根证书&证书链

上面身份认证/证书/签名部分,是站在网络协议设计者的角度倒推导如何设计安全的网络的,所以有些复杂。

但是如果从互联网用户的角度出发,其实就是一个逻辑或者说哲学问题:在不安全/不可信的互联网环境中,我该如何信任主机。

具体到当前的案例中,问题就是,在访问example.com时,我该如何确认相信,对方就是example.com而不是网络链路上的攻击者伪造的。

标准制定者们给出的答案是,互联网上的都不可靠,需要在操作系统和浏览器中直接内置受信任机构的根证书。这些证书是由操作系统和浏览器厂商信任的。

利用RSA算法,我们可以在系统中内置几个权威机构的根证书,权威机构去认证example.com的身份,然后给example.com颁发证书。

当example.com返回证书给我时,我(客户端)会查看证书的颁发者是否受操作系统/浏览器信任,如果已经信任,那么使用内置的公钥解密签名并验证,如果example.com证书的颁发者不受信任,那么向上找到上一级颁发者,直到被操作系统内置证书验证为止。如果根证书也不被信任,那么浏览器会警告当前网站证书不受信任,并阻止用户访问。

这里可以随便找一个支持Https的网站实际查看其证书。

小结

以上就彻底解决了互联网上的信任和身份认证问题。

自从引入RSA之后,问题仿佛接踵而至,中间人攻击方式也复杂起来,然而我必须强调,后面的一系列问题,他们本来就存在于不安全的网络环境中,并不是引入非对称加密导致的。

只不过在RSA之前,不安全的网络环境根本没资格考虑这么复杂的攻击方式,因为连简单的攻击都应对不了。

扩展——RSA工作原理简介

如何生成一对RSA密钥?

  1. 首先选择两个大素数 pq

pq 有多大呢?以现在主流的RSA 2048密钥标准,p * q 结果要为2048 bit的整数,换成十进制数约为616位。

  1. 计算 n = p * q 以及 z = (p-1)(q-1)

  2. 选择 e < n , 使 ez 互素,即没有公因数(除了1)

  3. d , 使得 ed mod z = 1

最终, (n, d) 为私钥, (n, e) 为公钥

如何使用公钥加密,私钥解密?

  1. 使用公钥 (n, e) 加密 假设要传递的信息原文为 m ,且 m < n , 计算
c=memodn(1)c = m^e \bmod n \tag{1}

c 即为密文。

  1. 使用私钥 (n, d) 解密 计算 cdmodnc^d \bmod n

其中 c 为公钥加密后的密文, 得到的结果就是原文 m , 即

m=cdmodn(2)m = c^d \bmod n \tag{2}

原理

由公式(1)(2)可得:

(memodn)dmodn=m(3)(m^e \bmod n)^d \bmod n = m \tag{3}

证明如下:

首先,mod 运算有如下规则:

((amodn)+(bmodn))modn=(a+b)modn((a \bmod n) + (b \bmod n)) \bmod n = (a+b) \bmod n
((amodn)(bmodn))modn=(ab)modn((a \bmod n) - (b \bmod n)) \bmod n = (a-b) \bmod n
((amodn)×(bmodn))modn=(a×b)modn(4)((a\bmod n) \times (b \bmod n)) \bmod n = (a \times b) \bmod n \tag{4}

由(4)可得:

(amodn)bmodn=abmodn(a\bmod n)^b \bmod n = a^b \bmod n

于是,公式(3)左边:

(memodn)dmodn=medmodn(5)(m^e \bmod n)^d \bmod n = m^{ed} \bmod n \tag{5}

有如下定理:

n = p * q 以及 z = (p-1)(q-1) ,则:

abmodn=abmodzmodna^b \bmod n = a^{b \bmod z} \bmod n

于是对于公式(5)

medmodn=medmodzmodn(5)m^{ed} \bmod n = m^{ed \bmod z} \bmod n \tag{5}

因为在生成密钥时,我们选择 ed mod z = 1

medmodzmodn=mmodnm^{ed \bmod z} \bmod n = m \bmod n

又因为 m < n ,所以

mmodn=mm \bmod n = m

公式(3)得以证明。