HTTPS 是一种网络通信协议,可确保数据在使用者的电脑和网站之间传输时的安全性。本文深入探讨 HTTPS 背后的技术原理,包括对称加密、非对称加密和 TLS,展示为何 HTTPS 是确保网络通信安全的关键所在。
HTTP 是什么?
在了解什么是 HTTPS 之前,我们需要先稍微了解 HTTP 是什么,HTTPS 其实就只是有加密版本的 HTTP。
HTTP 是超文本传输协议 (Hypertext Transfer Protocol),通过一些事先定义好的格式来传递数据。许多通过网络的数据传输都是通过 HTTP 协议达成的,例如浏览网页以及 API 调用。
HTTP 的信息主要分为两种:请求 (request) 和响应 (response)。当我们在网页上进行交互时,会产生 HTTP 请求和 HTTP 响应。举例来说,当我们点击超链接或是在 Google 搜索内容时,会发出 HTTP GET 请求到服务器,服务器会处理这些请求并响应 HTML 作为 HTTP 响应,让浏览器可以渲染页面。
一个 HTTP 请求可能像这样:
GET /hello.txt HTTP/1.1
User-Agent: curl/7.63.0 libcurl/7.63.0 OpenSSL/1.1.l zlib/1.2.11
Host: www.example.com
Accept-Language: en
这个 HTTP 请求会由浏览器产生,并通过网络发送给服务器。
一个 HTTP 响应可能像这样:
HTTP/1.1 200 OK
Date: Wed, 30 Jan 2019 12:14:39 GMT
Server: Apache
Last-Modified: Mon, 28 Jan 2019 11:17:01 GMT
Accept-Ranges: bytes
Content-Length: 12
Vary: Accept-Encoding
Content-Type: text/plain
Hello World!
注意 HTTP 是明文传递的,使用 HTTP 表示任何人都可以读取请求和响应的内容,包括敏感信息,例如:信用卡号、帐号密码等,所以 HTTP 是不安全的。
HTTPS 是什么? 和 HTTP 的差别是什么?
HTTPS 是安全版本的 HTTP。HTTP 和 HTTPS 的差别在于,HTTPS 使用 TLS 协议来加密 HTTP 请求和响应。
HTTP 不安全的理由主要有三个:
-
因为 HTTP 的数据是明文传递的,也就是说当我们在使用 HTTP 时,我们的敏感信息如密码、信用卡号是未加密的,可以被任何第三者看到。
-
因为 HTTP 没有身份验证的功能,我们没办法确定通信的对方就是他所宣称的身份。
-
因为 HTTP 没有可靠的验证内容的方法,我们没办法确定通信的内容没有被窜改过,也就是所谓的中间人攻击。
TLS 可以有效的解决这些问题。使用了 TLS 协议,会有以下的好处:
-
加密:客户端和服务器之间传输的所有数据都是经过加密的,第三者即使拦截数据也只能看到一串乱码,没办法知道通信的内容。
-
身份验证:通过 TLS 证书的机制,可以验证对方的身份。
-
完整性验证:通过 MAC (Message Authentication Code,消息验证码) 的机制,可以保证数据在传递过程中没有被篡改。
使用 HTTPS 协议的网站 URL 以 https://
开头,使用 HTTP 协议的网站 URL 则是以 http://
开头。
通过 HTTPS 传递的内容,攻击者只能看到一堆随机的字符串,而不是明文。举例来说,用 HTTP 传输的内容,攻击者可以看到类似以下的明文信息:
GET /hello.txt HTTP/1.1
User-Agent: curl/7.63.0 libcurl/7.63.0 OpenSSL/1.1.l zlib/1.2.11
Host: www.example.com
Accept-Language: en
相反地,用 HTTPS 传输的内容,攻击者只能看到类似以下的加密字符串:
t8Fw6T8UV81pQfyhDkhebbz7+oiwldr1j2gHBB3L3RFTRsQCpaSnSBZ78Vme+DpDVJPvZdZUZHpzbbcqmSW1+3xXGsERHg9YDmpYk0VVDiRvw1H5miNieJeJ/FNUjgH0BmVRWII6+T4MnDwmCMZUI/orxP3HGwYCSIvyzS3MpmmSe4iaWKCOHQ==
整个 HTTPS 信息都经过完整的加密,包含 HTTP 方法、状态码、URL、query string、HTTP headers (包含 cookie)、HTTP body 等全部都有加密。
值得注意的是,域名 和 IP 是在 HTTPS 中没有被加密的。
要了解 HTTPS 和 TLS 如何运作,需要对加密有基本的了解,下面就来介绍加密的基本常识。
加密的原理
加密是一种将明文转变成难以理解的密文的过程。通常加密会需要一个密钥 (key),解密也会需要一个 key,只有通过解密的 key 才能够将密文转变回明文。Key 通常是一个字符串。
加密主要有两种形式:对称加密 (symmetric encryption) 和 非对称加密 (asymmetric encryption)。
对称加密 (Symmetric Encryption)
对称加密中,加密和解密都是使用同一把 key。下图展示了对称加密的原理:
非对称加密 (Asymmetric Encryption)
非对称加密中,有两把 key,一把 key 是公开的,任何人都可以拿到,所以又称为公钥 (public key);另外一把 key 是保密的,只有 key 的主人拥有这把 key,因此又称为私钥 (private key)。非对称加密也被称为公钥加密。
非对称加密有两个主要的使用方式:
-
用公钥加密的数据,只能用私钥解开。因此通过非对称加密算法,所有人都可以将加密过的信息传给私钥的拥有者,只有私钥的拥有者能解开。
-
用私钥加密的数据,只能用公钥解开。因此通过非对称加密算法,所有人都可以用公钥解开一条由私钥加密过的信息,确认这条信息是由私钥的拥有者所送出。
下图展示了非加密对称的原理:
SSL/TLS 是什么?
SSL (Secure Socket Layer) 是一种加密协议,可以让客户端和服务器端之间的通信保持加密。
TLS (Transport Layer Security,传输层安全性协议) 是 SSL 的更安全版本,目前网络的世界中的安全通信大多采用 TLS,TLS 1.3 是最新的版本。
在 HTTPS 中,使用了 TLS 保护数据传输的安全。
由于历史的因素,很多时候 SSL/TLS 两个词会被混用。下图是 SSL/TLS 的发展历史图:
TLS 的功能如下:
-
加密:TLS 对内容加密,因此即使第三方拦截了数据,也没办法判读内容,只能看到一串乱码。
-
身份验证:TLS 用证书确定通信的服务器的身份。
-
完整性验证:TLS 确保数据在传输的过程中没有被窜改。
TLS 的原理
建立 TLS 连接的第一步是由 TLS 握手开始。客户端和服务器在 TLS 握手中会执行以下几件事:
-
指定使用的 TLS 版本。
-
指定使用的密码套件(cipher suite),其中包含了用于会话密钥加密的算法、密钥交换算法,以及消息认证码(MAC)验证算法。
-
客户端使用服务器提供的 TLS 证书验证服务器的身份。
-
握手完成后生成会话密钥 (session key) 用来加密客户端和服务器之间的信息。
在 TLS 握手的过程中,首先服务器会提供 TLS 证书向客户端证明其身份,接着通过密钥交换的机制产生用来加密信息的会话密钥。后续的数据会被加密,并且使用 MAC 算法进行签名,接受方可以用 MAC 验证数据是否有被篡改。
接下来会介绍 TLS 中如何做到加密、身份验证和完整性验证。
混合式加密
对称式加密的好处是快速且能进行双向通信,但是如果没办法让通信的两端安全的使用同一把密钥,那么对称式加密也无用武之地。
非对称加密的好处是能实现安全的单向通信,但是缺点是速度慢,也不适合双向的通信。那该怎么克服这些缺点,让我们能够安全又快速的传递信息呢?
在 TLS 中,同时使用了对称加密和非对称加密两种技术,来实现安全又高效的双向通信。
在 TLS 中,会使用会话密钥来进行信息的加密。会话密钥是对称式加密,客户端和服务器端拥有同样的会话密钥,可同时用于加密和解密。
同时,为了要让客户端和服务器端两边有同样的会话密钥,客户端和服务器端会使用密钥交换算法 (运用到非对称加密技术) 来安全地产生会话密钥。
SSL/TLS 证书
SSL 证书,准确来说是 TLS 证书,是在 SSL/TLS 连接中用来进行服务器身份验证的一份文件,是由 CA (Certificate Authority,证书颁发机构) 发给个人或企业。
SSL/TLS 证书包含了以下重要信息:
-
域名 (domain) 的拥有者是谁
-
服务器的公钥(并且被 CA 的私钥签署 (sign) 过)。
为了要能够进行服务器的身份验证,首先在网站需要在服务器上安装 SSL/TLS 证书,然后在 TLS 握手的过程中传给客户端。比如浏览器通常内建 CA 的公钥,因此可以验证被 CA 签署过的证书,达到验证服务器身份的目的。
按照支持的域名类型来区分,SSL证书可以分为单域名证书、通配符证书以及多域名SSL证书等。
在证书颁发机构(CA)签发 TLS 证书之前,会进行不同程度的身份验证。依据验证的严格程度,TLS 证书可分为域名验证(Domain Validation, DV)、组织验证(Organization Validation, OV)和扩展验证(Extended Validation, EV)SSL 证书。
MAC
加密过后的信息可以用 MAC (Message Authentication Code,消息验证码) 的技术来达到完整性验证。常见的有 MD5/SHA 等算法。
会话密钥是什么?
会话密钥(session key)的作用是加密 HTTPS 中服务器端和客户端之间沟通的信息。
要使用会话密钥对 HTTPS 的信息加密,需要先进行TLS 握手,TLS 握手的其中一个目的就是让客户端和服务器端协商产生会话密钥。
在 TLS 握手的过程中,会产生下列的随机数据,这些随机数据会被用来产生会话密钥:
-
Client Random: 从客户端发送到服务器的随机字符串。
-
Server Random: 从服务器发送到客户端的随机字符串。
-
Premaster Secret(预主密钥): 一个随机字符串,经过一些运算得出。在某些版本的 TLS 握手中,客户端生成预主密钥并加密后送至服务器;某些版本的 TLS 握手中,客户端和服务器分别使用相同的算法和参数算出相同的预主密钥。
-
Master Secret(主密钥): 客户端和服务器通过组合 client random, server random, premaster secret 运算出主密钥。
客户端和服务器可以用主密钥(master secret) 计算出四个会话密钥(session key),分别是:
-
客户端写密钥(Client Write Key)
-
服务器写密钥(Server Write Key)
-
客户端写消息认证码密钥(Client Write MAC Key)
-
服务器写消息认证码密钥(Server Write MAC Key)
Client write key 是对称密钥,由客户端发送的信息会由 client write key 加密,在服务器端会由同一把 client write key 解密;同样的,Server write key 也是对称密钥,由服务器端发送给客户端的信息会由 server write key 加密,在客户端由浏览器或设备用同一把 server write key 解密。
Client/Server MAC Key 的作用是对信息进行签名。服务器用 server write MAC key 对发给客户端的信息进行签名,客户端收到信息后可以用自己的 server write MAC key 做验证;同样地,客户端用 client write MAC key 对发送给服务器端的信息进行签名,服务器用自己的 client write MAC key 进行验证。
TLS 握手是什么?
为了要使用 TLS 加密协议,我们要先启动 TLS 握手。
TLS 握手
每当用户使用 HTTPS 通信的时候,首先浏览器会和服务器通过 TCP 握手建立 TCP 连接。当 TCP 连接建立完成后,就会开始 TLS 握手。
TLS 握手期间会发生以下的事情:
-
指定使用的 TLS 版本 (TLS 1.0, 1.2, 1.3 等)
-
决定使用的密码套件( cipher suite )。密码套件是一组算法的组合:
-
密钥交换算法。
-
加密算法。
-
MAC 算法
-
-
验证服务器的身份
-
产生 session key(会话密钥)
TLS 握手的具体步骤根据密钥交换算法不同而有所差异。下面举 RSA 和 Ephemeral Diffie-Hellman 两种不同的密钥交换算法为例:
RSA Key Exchange
-
Client hello: 客户端向服务器端发送 “client hello”,其中包含客户端支持的 TLS 版本、客户端支持的密码套件、由客户端产生的随机字符串 client random。
-
Server hello: 服务器端回复 “server hello” 的信息,其中包含服务器的 SSL 证书、选择使用的密码套件、由服务器端产生的随机字符串 server random。
-
客户端用 SSL 证书验证服务器的身份。
-
客户端再产生一个随机字符串 premaster secret(预主密钥),并且用公钥加密过后传给服务器 (公钥来自于 SSL 证书)。因为用公钥加密过,只有拥有私钥的服务器端可以解密。
-
服务器用私钥解密 premaster secret(预主密钥)。
-
客户端和服务器端分别使用 client random + server random + premaster secret (预主密钥)产生 session key(会话密钥)。他们会得到同样的结果。
-
客户端向服务器端发送一条用会话密钥加密过的 “finished” 的消息。
-
服务器端向客户端发送一条用会话密钥加密过的 “finished” 的消息。
-
握手完成,之后的信息都使用会话密钥加解密。
Ephemeral Diffie-Hellman Key Exchange
-
Client hello: 客户端向服务器端发送 “client hello”,其中包含客户端支持的 TLS 版本、客户端支持的密码套件、由客户端产生的随机字符串 client random。
-
Server hello: 服务器端回复 “server hello” 的信息,其中包含服务器的 SSL 证书、选择使用的密码套件、以及服务器用私钥加密过的 client random / server random / DH 参数,这个加密的动作等于数字签名,可以验证服务器的身份。
-
验证:客户端用公钥解开服务器加密的信息,同时验证服务器的身份。
-
客户端将其 DH 参数传给服务器。
-
客户端和服务器端各自用 DH 参数计算出 premaster secret(预主密钥)。和 RSA 不同,客户端并没有将 premaster secret (预主密钥)加密后送给服务器端。
-
客户端和服务器端分别使用 client random + server random + premaster secret(预主密钥) 产生 session key(会话密钥)。他们会得到同样的结果。
-
客户端向服务器端发送一条用会话密钥加密过的 “finished” 的消息。
-
服务器端向客户端发送一条用会话密钥加密过的 “finished” 的消息。
-
握手完成,之后的信息都使用会话密钥加解密。
前向保密 (Forward Secrecy)
前向保密的意思是:即使私钥被泄露,加密过的数据也维持着加密的状态,不会被破解。
在 RSA 中,由于 premaster secret (预主密钥)是通过公钥加密,因此私钥暴露表示可以解密所有的 premaster secret(预主密钥),再加上 client random 及 server random 都是明文传递,可以计算出所有 session key(会话密钥),因此 RSA 不具有前向保密性。
在 Ephemeral Diffie-Hellman 握手中,私钥只用于验证服务器身份,并且每个会话都产生独立的 session key(会话密钥),因此即使私钥暴露也没办法解密过去的信息,具有前向保密能力。
SNI (Server Name Indication) 是什么?
SNI (Server Name Indication) 是 TLS 协议的一部份,其作用在于当多个不同的域名被托管在同一IP地址上时,服务器能够根据客户端在建立HTTPS 连接时发送的请求信息返回相应的 TLS 证书,从而确保 HTTPS 连接能够正确且安全地建立。
之所以要介绍 SNI 的原因是,它关系到一个很细节但很重要的知识:HTTPS 没有加密域名。
在了解 SNI 是什么之前,我们需要先了解一个常见的情境:很多个网站可以共享一个 IP,举例来说 https://www.example.com 和 https://www.another-website.com 可能背后是由同一台服务器服务,有着相同的 IP 地址。
在这个情况下,如果要进行 HTTPS 连接会有一个问题,就是服务器不知道要给客户端哪一张 SSL 证书,因为服务器不知道客户端打算跟哪一个网站连接。其中要特别注意的是,在 HTTPS 连接中,要先完成 TLS 握手才能进行真正的 HTTP 通信,所以我们没办法通过 HTTP 请求的内容去判断要跟哪一个域名连接。
如果服务器给了错误的证书,客户端可能会显示不是安全连接。
SNI 就是要解决这个问题。当客户端和服务器连接时,客户端会告诉服务器目的地的域名,让服务器端可以响应相对应的证书。
具体来说,SNI 会在 client hello 的信息中包含域名,作为 TLS 握手的第一步。要特别注意的是,client hello 是明文的,所以 HTTPS 中域名是没有加密的。
ESNI (Encrypted SNI)
SNI 是 TLS 握手过程中的 client hello 信息的一部分,包含了客户端想要连接的服务器域名,服务器收到信息后应该回复相对应的证书。
注意到 SNI 是未加密的,只有在整个 TLS 握手完成后,通信的内容才是有加密的。这表示任何人都可以知道客户端正在跟哪个域名进行通信,这让攻击者有机可趁,例如攻击者可以制作域名名称和内容相近的钓鱼网站。
ESNI (Encrypyed ESI) 通过加密来保护使用者。服务器将公钥加到他的 DSN 纪录中,客户端可以用公钥对 ESI 的部分加密,只有特定的服务器可以解密。
然而光靠 ESNI 并没有办法阻止有心人士知道客户端正在跟哪个网站连接。由于 DNS 是明文的,所以第三者还是可以知道你打算跟哪个网站连接。一些机制可以提供进一步的保护,像是 DNSSEC 可以确定我们连到一个可信的 DNS 服务器;DNS over HTTPS(DoH)和 DNS over TLS(DoT)则分别通过HTTPS协议和TLS协议加密DNS查询过程,从而确保整个 DNS 查询内容在传输过程中是受到保护和加密的,防止数据被窃取或监听。
HTTPS FAQ
下面讨论一些 HTTPS 的常见问题:
HTTPS 有加密哪些东西?
下面是一个 HTTP 请求的范例:
因为 HTTP 是完全没有加密的,所以这些东西都会被看光光:
-
IP
-
域名
-
完整的请求 URL,包含 query string
-
HTTP method (GET/POST/…)
-
HTTP status code (HTTP 状态码)
-
所有的 HTTP header,包含 cookie
-
HTTP body
HTTPS 加密后的请求范例如下:
可以看到 URL 的后半段包含 query string 是有被加密的 (但是域名没有);HTTP method 和 status code 是有加密的;所有的 HTTP header (包含 cookie) 都是有被加密的;所有的 HTTP body 都是有被加密的。
HTTPS 没有加密哪些东西?
在 HTTPS 中,没有被加密的东西有 IP 地址和域名。
原因如下:
-
在 DNS lookup 的过程中,如果采用未加密的 DNS lookup,IP 跟域名就是明文传递的。
-
在 TLS 握手的过程中,SNI (Server Name Indication) 会在 client hello 的信息中夹带明文的域名,以便服务器可以返回正确的 TLS certificate 供客户端认证。
为什么 HTTPS 没有加密域名?
主要是为了支持 SNI (Server Name Indication),目的是让多个域名可以绑定在同一个 IP 地址上。
在 TLS 握手的过程中,SNI (Server Name Indication) 会在 client hello 的信息中夹带明文的域名,以便服务器可以返回正确的 TLS certificate 供客户端认证。
另外,DNS lookup 的过程中也会泄露域名的信息,即使使用了 DNSSEC 也一样。DNSSEC 只能保证拿到正确的 IP 地址,不能保证这个信息是有被加密的或是完整性,且如果失败的话浏览器也不会有提示。
HTTPS 如何预防 DNS spoofing?
DNS Spoofing,又称作 DNS Cache Poisoning,是将错误的信息放进 DNS cache,使得 DNS lookup 的结果错误,进而将用户导到错误的网站。DNSSEC 是为了解决这个问题而生的,但尚未受到全面采用。
如果网站使用了 HSTS (HTTP Strict Transport Security),可以强制浏览器在浏览网站的时候必须使用 HTTPS。这表示 DNS spoofing 的攻击者同时也需要破解 HTTPS,可以让攻击的难度提升不少。
HTTPS 和 HTTP/2 有什么关系?
HTTP/2 是 HTTP/1.1 的更新版协议,完成于 2015 年,主要目的是提升 HTTP 协议的效率和性能。
虽然 HTTP/2 并没有强制要求加密,但主流浏览器都只实现了加密的 HTTP/2,因此可以说 HTTP/2 强制使用 HTTPS。
如何决定是用 HTTPS 还是用 HTTP/2?
因为 HTTP/2 是基于 HTTPS 的,实务上在 TLS 握手的过程中,client hello 信息中可以通过 ALPN (Application Layer Protocol Negotiation,应用层协议协商) 表示客户端支持 HTTP/2,如果服务器也支持 HTTP/2 就可以顺利转而使用 HTTP/2。