网络协议(三):HTTPS 协议

92 阅读10分钟

为什么要有 HTTPS?

HTTP (除了最新的 HTTP/3 外),传输层是基于 TCP 协议的。TCP 建立连接时,有三次握手。三次握手完毕之后,TCP 连接就顺利建立了,就直接开始传输数据了,这时候数据是明文传输的,在明文传输下,理论上来讲,浏览器和服务器之间的任一节点,包括你家里的路由器、包括你购买宽带/流量的运营商,都可以“窃听”你们的数据,甚至还可以修改数据。这也是 HTTP 最不安全的地方。

HTTPS 的思想:浏览器在发送 HTTP 请求之前,先通过某种方式对其进行加密,然后再进行传输。服务器端收到数据之后,对其解密,读取真实内容,生成 HTTP 响应,同样对响应进行加密,然后传回给浏览器,浏览器收到数据之后,对其进行解密,得到真正的 HTTP 响应。这样就可以保证数据在传输过程中的安全性,无论是路由器还是运营商,都没有办法“窃听”你们的数据了。

所以 HTTPS 的作用就是:保证数据在互联网上传输的安全性,避免中间节点进行窃听和修改。

带着两个问题继续阅读

  • 问题1:浏览器和服务器是如何提前约定加密方案的?

  • 问题2:加密方案安全吗?会被破解吗?

SSL 和 TLS

HTTP + SSL = HTTPS

SSL(Secure Sockets Layer)是一种安全协议,用于在互联网上保护数据的传输安全。它工作在传输层,主要功能是通过加密技术,保护不同计算机之间的数据传输过程,防止敏感数据被窃取和篡改。

SSL 协议目前已经被继承为 TLS(Transport Layer Security),是一种安全性更高的传输层协议。下面统一以 TLS 为名称进行讲解。

TLS 是什么时候开始发挥作用的呢?

直接说结论:TSL 工作在传输层,在 TCP 3 次握手之后,传输数据之前

也就是说,在 TCP 协议中加入 TLS 之后,TCP 的三次握手成功之后就不会再立刻开始传输数据了,而是紧接着开始 TLS 的建立过程,也被称为 TLS 握手。

什么是 TLS 握手

TLS 握手的目的是给通信双方约定一个安全的加密方案,并且这个约定的过程(即 TLS 握手过程),即使被任何第三方窃听到,也无法解析出这个加密方案(密钥)。

首先了解一个概念:什么是对称加密和非对称加密?

对称加密:只有一个密钥,加/解密使用同一个密钥。

非对称加密:有两个密钥(公钥和私钥),公钥加密只能私钥解,私钥加秘密只能公钥解。

简单理解 TLS 握手过程:

  1. 服务器端用非对称加密方案生成一对公/私钥,私钥掌握在自己手里,谁也不告诉

  2. 浏览器发起请求,TCP 连接建立成功,TSL 握手过程开始

  3. 在 TLS 握手的过程中,服务器将自己的公钥交给浏览器端

  4. 浏览器端随机生成一个对称加密的密钥(预主密钥 Pre-Master Secret),并使用服务器端发送过来的公钥进行加密

  5. 浏览器将这个加密后的预主密钥传给服务器端,服务器端使用私钥进行解密

  6. 这样,浏览器端和服务器端就都知道这个约定的用于对称加密的预主密钥了。

注意:预主密钥并不是长期有效的,它只在 TSL 的会话生命周期内有效。

2.png

这就是 TLS 握手的过程吗?不,当然没这么简单了,我们还没有考虑一个非常巧妙的攻击手段:中间人攻击。

中间人攻击

假设现在我们从电脑上访问百度,如果有一个中间人在我的路由器端,或者运营商端,或者任何一个中间节点上截取了我的请求,刚不是提到服务器端需要返回给我们公钥吗,中间人他自己也生成一套公/私钥,然后将自己的公钥返回给我,这样我就与中间人之间建立了一条我以为“安全”的连接了,此时我以为我连接的是百度服务器,其实我连接的是中间人...那么此时中间人可以做任何事情了,如果他人品比较好的话,他可以默默当一个代理,我要访问百度,他就去帮我访问百度,然后把结果返回给我,勤勤恳恳做一个“中间商”。当然,我们知道做这种攻击的人人品往往不会太好,所以他们可以做更坏的事情,例如伪造一个银行网页返回给我,让我填写账号和密码,这样的话...后果就不堪设想了。

那么如何防止中间人攻击呢?其中的核心就是:我们需要保证我们访问的就是目标服务器。例如,当我们访问百度时,我们需要确保在 TLS 握手时,给我们公钥的人就是百度,而不是任何其他人。

那么这个应该如何去保证呢?这就不得不提到接下来的几个概念了,数字证书,以及证书权威机构(Certificate Authority,简称 CA)。

数字证书、CA

数字证书是由证书权威机构(CA)颁发的一个用于证明身份的证书,当然其中还包含了该用户的公钥等信息。还是以百度为例,假设百度需要给 www.baidu.com 这个域名申请一个数字证书,他需要在生成公钥/私钥后,将自身的信息(包括域名、公司名称、公钥信息等)发给某个证书权威机构(CA),让 CA 给自己颁发一个数字证书。CA 需要验证百度的真实身份和域名,一切都验证通过后,CA 才会给百度颁发这么一个数字证书。那么之后,不管是谁用浏览器访问 www.baidu.com 的时候,百度都会将刚才那个 CA 颁发的数字证书发送给用户,既可以用来自证身份,同时还顺便告诉了用户自己的公钥。

数字证书如何保证不能伪造呢,难道中间人不能伪造一个数字证书发送给用户吗?

一个证书会包含以下内容:

  • 颁发对象:这个证书是颁发给百度的,并且只对域名 www.baidu.com 有效

  • 颁发者:这个证书是由什么机构颁发的

  • 有效期:一旦过期,证书将不被信任

  • 服务器端的公钥

  • 指纹:整张证书经过哈希计算后得到的特征值,主要与后面会提到的签名一起工作,起到防篡改的作用

  • 签名:证明数字证书有效的关键信息

CA 作为权威机构,其本身也是有一对公钥/私钥的,它在颁发数字证书的时候,会用自己的私钥对证书的指纹进行加密,生成的这段加密数据,就是该证书的签名了!

那么我们浏览器是如何验证证书的真伪呢?

我们只需要使用 CA 的公钥对签名进行解密,看看得到的值是不是跟证书的指纹是一样的,这样就 OK 了,只要是一样,说明这个证书一定是 CA 颁发的。

我们浏览器是从哪里拿到 CA 的公钥呢?

浏览器或操作系统已经内置了世界权威的 CA 的数字证书(证书里就包含了其公钥)了,点击浏览器的 设置 -> 隐私设置和安全性 -> 安全 -> 管理设备证书,可以查看当前系统内置的所有 CA 证书。

数字证书中有一段签名,该签名是 CA 使用其私钥对证书指纹进行加密后得到的值,我们浏览器使用 CA 的公钥对该签名进行解密后,与该证书的指纹进行对比,就可以知道证书是否被篡改或者伪造了。

当然,这里要多提一嘴,我们作为客户端,需要保证自己的电脑里保存的都是值得信任的 CA 根证书,因为信任某 CA 就代表信任了该 CA 颁发的所有数字证书,如果有人/软件想在你的电脑里安装来历不明的 CA 证书,那你就要保持警惕了...

即使数字证书不能被伪造,从概念上看他是公开的,难道中间人不能直接把这个证书颁发给用户吗?

服务器端除了公钥外,自身还保存有一份私钥的,而中间人是拿不到这个私钥的,因为它被服务器雪藏起来,不会发送到互联网中的...那么如果中间人用服务器的证书返回给用户,用户采用服务器的公钥对自身生成的预主密钥进行加密后,返回给中间人的时候,中间人就一脸懵逼了,因为这个密钥它解不开呀,它没有私钥的,所以这个问题就完美解决了。

完整的 TLS 握手流程

  1. 密钥生成:服务器端生成一对非对称加密的公钥和私钥。私钥安全地保存在服务器上,公钥则用于后续的密钥交换过程。

  2. TCP 连接建立:浏览器发起请求,首先与服务器建立TCP连接。TCP三次握手完成后,TLS握手过程开始。

  3. Client Hello:客户端向服务器发送 Client Hello 信息,告知自己想要建立一条 TLS 连接,并告知自己支持的加密算法

  4. Server Hello:服务器向客户端发送一个 Server Hello 的回应,并选择一个加密算法,同时给客户端发送自己的数字证书(包含服务器的公钥)。

  5. 密钥交换:客户端验证服务器发来的数字证书,验证通过后生成预主密钥 Pre-Master Secret,然后使用服务器的公钥,将预主密钥进行加密后,发送给服务器。

  6. 密钥解密:服务器用自己的私钥进行解密,得到预主密钥

  7. shared secret:客户端和服务器都通过预主密钥,进行一些相同的计算后,得到后续通信时使用的对称加密密钥 shared secret

  8. 然后 TLS 就建立成功了,接下来双方都用这个 shared-secret 进行加密通信。

为什么不全程使用非对称加密呢?

因为对称加密效率更高,尤其是在大量数据的时候,对称加密比非对称加密整整快几个数量级,所以真正数据传输的过程选用了对称加密。

HTTPS 总结

HTTPS 解决了两个问题:

  1. 数据传输过程中的安全问题,因为它对数据进行了加密,只有浏览器和服务器可以对其进行解密。

  2. 浏览器对服务器的信任问题,数字证书以及其中的数字签名,保证了我们访问的就是我们想要访问的服务器,不可能被钓鱼网站欺骗,也不可能被中间人攻击所欺骗。(前提是电脑上没有安装来历不明的证书)

nginx 如何配置 https

server {
   listen       443 ssl;
   server_name  techcorner.cn;

   # 服务器证书
   ssl_certificate      /path/to/server.cer;
   # 服务器私钥
   ssl_certificate_key  /path/to/server.key;

   # 配置SSL会话缓存的大小和存储位置
   ssl_session_cache    shared:SSL:1m;
   ssl_session_timeout  5m;

   # 加密套件,指定服务器支持的加密算法
   ssl_ciphers  HIGH:!aNULL:!MD5;
   # 指定服务器在与客户端建立SSL连接时使用的加密算法。
   # 默认情况下,nginx会使用客户端提供的加密算法进行通信。
   # 但是,通过设置为on,服务器将优先使用自己配置的加密算法。
   ssl_prefer_server_ciphers  on;

   location / {
       root   html;
       index  index.html index.htm;
   }
}

参考资料

万字长文,带你彻底搞懂HTTPS