HTTPS 是如何保证传输安全的?
纸上得来终觉浅,除开原理部分外,本文还提供了 HTTP 和 HTTPS 的实战教程,帮助你从 0 开始搭建一个 HTTPS 加密的 Web 服务器,如果按照实战教程一步一步走下来,那么你将对 HTTPS 有更进一步的把握。
好了,废话不多说,直接进入正题吧。
什么是 HTTPS ?一句话
HTTPS = HTTP + SSL
HTTPS 并不是一个全新的协议,而是在 HTTP 的基础上,通过 SSL 增加了一层加密协议,从而大大增加了 HTTP 协议的安全性。
所以在正式了解 HTTPS 之前,我们需要先了解 HTTP。

GET / HTTP/1.1 Accept: text/html,... Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cache-Control: no-cache Connection: keep-alive Host: httpbin.org Pragma: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
- GET 表示请求方法,常见的 HTTP 请求方法有 GET、POST、PUT、DELETE 等…
- GET 后面的 /表示请求路径,这里我们访问的根路径,所以显示为 /。如果你访问 httpbin.org/get的话,这里显示的就是 /get了
- HTTP/1.1 表示使用的 HTTP 协议版本,现在常用的有 HTTP/1.1 和 HTTP/2,当然还有更先进的 HTTP/3,这里就不过多展开了
- 下面的 9 行全部都是 HTTP header,每一个 header 包含 name 和 value,之间用冒号分隔开。
HTTP/1.1 200 OK Date: Sat, 08 Apr 2023 16:28:43 GMT Content-Type: text/html; charset=utf-8 Content-Length: 9593 Connection: keep-alive Server: gunicorn/19.9.0 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true Body...
- HTTP/1.1 指的协议版本,响应和请求的协议版本是一致的
- 200 OK 代表返回的响应码,表示这个响应是符合预期的。另外还有非常常见的返回码 404 NOT FOUND ,大家应该或多或少听说过,它表示服务器告诉你你访问的这个资源不存在
- 后面 7 行全部是 HTTP header,同样每一个 header 包含 name 和 value,之间用冒号分隔开。
- 最后是 HTTP Body,也就是响应体,即服务器返回给你的内容主体,浏览器正是根据响应体来渲染页面的
对于普通的 HTTP 协议,在建立完 TCP 连接之后,就直接开始传输数据了,这时候数据是明文传输的,这也是 HTTP 最不安全的地方。
明文传输是什么概念呢?我们知道,浏览器和服务器之间,是存在很长一条路线的,你在家里通过浏览器访问网页的时候,数据会从你的电脑,传到你家里的路由器,再到光猫,到运营商,到互联网….直到最后才到服务器。在明文传输下,理论上来讲,浏览器和服务器之间的任一节点,包括你家里的路由器、包括你购买宽带/流量的运营商,都可以“窃听”你们的数据,甚至还可以修改数据。
听起来不够直观?打个比方,近代时期,战场上打仗时,部队之间会通过电台进行交流。如果通过明文进行交流,那么非常危险,敌军可以打开电台进行窃听,偷取你的军事情报,这样的事也屡见不鲜了…那么他们是如何解决这个问题的呢?两个部队之间提前约定一个加密的方案,在传数据之前,先把它进行加密再传输,另一端收到数据之后,按照事先约定的方案进行解密,然后读取就可以了。这样即使敌军开始窃听,也只能听到加密后的情报,如果无法对其破解的话,得不到任何有效信息。
没错,这就是 HTTPS 的思想,浏览器在发送 HTTP 请求之前,先通过某种方式对其进行加密,然后再进行传输。服务器端收到数据之后,对其解密,读取真实内容,生成 HTTP 响应,同样对响应进行加密,然后传回给浏览器,浏览器收到数据之后,对其进行解密,得到真正的 HTTP 响应。这样就可以保证数据在传输过程中的安全性,无论是路由器还是运营商,都没有办法“窃听”你们的数据了。
说到这里,想必你已经知道 HTTPS 的一大作用了,它可以保证数据在互联网上传输的安全性,避免中间节点进行窃听和修改。
当然,聪明的你还可能会想到一些问题,例如:
- 战场上军队之间是提前约定好加密方案的,但是咱们任意一个浏览器都可以随时访问网页,没有办法提前约定加密方案呀,那是怎么做到的呢?
- 战场上经常出现敌军对另一方部队之间的电台加密进行破解的事情,破解完成之后,还是能够窃听到数据,那 HTTPS 的这个加密方案到底安全吗,会被破解吗?
别急,这些问题,你都可以在本文中得到答案。
上面提到了对 HTTP 进行加密的思想。在 HTTPS 的具体实现中,这个加密方案即是大名鼎鼎的 SSL(Secure Sockets Layer)。
那么 TLS 是什么时候开始发挥作用的呢?答案很简单,在三次握手之后,传输数据之前。
也就是说,在 TCP 协议中加入 TLS 之后,三次握手成功之后就不会再立刻开始传输数据了,而是紧接着开始 TLS 的建立过程,也被称为 TLS 握手。
TLS 握手是干嘛呢?或者说为什么需要 TLS 握手呢?上面提到,在战场上,两个部队之间会提前约定好加密的方案,例如面对面用纸互相写下加密方案,然后在一段时间之内的电台通信统一用这个加密方案,这样能一定程度上保证电台通信的安全性。但是 TLS 中我们并没有这样一个“面对面”的机会,咱们总不可能在访问网页之前,人肉跑到服务器的维护者那边去跟他约定加密方案吧。出于这个目的,TLS 握手便出现了。所以我们可以说,TLS 握手的目的是给通信双方约定一个安全的加密方案(可以理解为商量一个只有双方知道的加密密钥)
知道了 TLS 握手的目的,接下来我们需要知道它具体是怎么做的。首先,我们肯定不能直接明文传输加密方案(密钥),不然这个密钥在传输过程中就直接被第三方获取了,那么加密将没有任何意义。也就是说,TLS 握手需要做到:通信双方可以约定一个共同的加密方案(密钥),并且这个约定的过程(即 TLS 握手过程),即使被任何第三方窃听到,也无法解析出这个加密方案(密钥)。
是不是听起来很神奇,那到底是怎么做到的呢?这就不得不提到密码学中非常经典的两个概念:对称加密和非对称加密。


简单说说其思想吧,目前流行的非对称加密算法 RSA 基于的原理其实就一句话:我们目前还没有很好的办法对一个很大的数做因式分解,例如你在心里默默想一个很大的质数 p 和 质数 q,算出其乘积 n,那么向外公开 n 的话,外部人员是很难找出 p 和 q 的(只能暴力尝试,而当这样的数够大够多的时候,以现在的计算机算力也需要几百上千年的时间才能破解了)。对密码学感兴趣的同学,可自行进一步了解。
有了非对称加密,事情就变得有意思起来了。见下图,服务器端用非对称加密方案生成一对公/私钥,私钥掌握在自己手里,谁也不告诉;在 TLS 握手的过程中,服务器将自己的公钥交给浏览器端,浏览器端在心里默默想出一个对称加密的密钥后,将这个密钥用服务器端的公钥进行加密,然后再传回给浏览器端;浏览器端收到这个数据之后,用自己的私钥将其进行解密,就能够得到刚才浏览器心里默念的那个对称密钥了。这样这个问题就完美的解决了,两边可以心有灵犀的拿到这个对称密钥,而不用担心被任何第三方窃取到了。


CA 需要验证百度的真实身份,并且他确实拥有 www.baidu.com 这个域名,一切都验证通过后,CA 才会给百度颁发这么一个数字证书。那么之后,不管是谁用浏览器访问 www.baidu.com 的时候,百度都会将刚才那个 CA 颁发的数字证书发送给用户,既可以用来自证身份,同时还顺便告诉了用户自己的公钥。
- 数字证书如何保证不能伪造呢,难道中间人不能伪造一个数字证书发送给用户吗?
- 即使数字证书不能被伪造,从概念上看他是公开的,难道中间人不能直接把这个证书颁发给用户吗?
下面会一一回答这几个问题。正式回答之前,先来看看数字证书里究竟有哪些内容。
既然上面提到百度,我们就以百度为例,我们使用浏览器访问百度,可以在地址栏左边看到一个小锁,点击后,就可以查看百度的数字证书


- 颁发对象:这个证书是颁发给百度的,并且只对域名 (www.)baidu.com 有效
- 颁发者:这个证书是由 GlobalSign 颁发的。(GlobalSign是一家全球知名的证书权威机构)
- 有效期:这个证书的有效期是从 2022 年 7 月到 2023 年 8 月。(一旦过期,证书将不被信任)
- 指纹:指纹是整张证书经过哈希计算后得到的特征值,主要与后面会提到的签名一起工作,起到防篡改的作用
$ openssl x509 -noout -text -in baidu.com.cer Certificate: Data: Version: 3 (0x2) Serial Number: 44:17:ce:86:ef:82:ec:69:21:cc:6f:68 Signature Algorithm: sha256WithRSAEncryption Issuer: C=BE, O=GlobalSign nv-sa, CN=GlobalSign RSA OV SSL CA 2018 Validity Not Before: Jul 5 05:16:02 2022 GMT Not After : Aug 6 05:16:01 2023 GMT Subject: C=CN, ST=beijing, L=beijing, OU=service operation department, O=Beijing Baidu Netcom Science Technology Co., Ltd, CN=baidu.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:aa:2f:cc:41:8d:25:ae:83:e9:f4:27:c4:00:b3: 39:6f:0e:98:2a:55:7d:07:e5:80:49:82:fa:d3:d3: 85:98:b5:df:7b:6f:bb:02:dd:ed:78:e4:0c:07:2b: 9e:1e:86:4b:f6:6a:86:58:d7:57:6f:21:59:11:d8: 6f:96:6e:d2:de:36:28:f6:b4:e3:ce:95:32:29:00: c1:65:8e:69:b0:00:fe:52:37:f4:88:3f:8b:6d:0f: bb:f0:ec:c5:c0:31:ef:ad:b5:0c:06:66:ad:be:dc: 43:13:c4:66:b0:5d:cf:56:53:e2:d1:96:82:1c:06: bb:9b:5f:ed:60:8d:d2:ed:f3:d2:50:ee:bb:cd:b2: 36:97:c8:ce:7b:d2:4b:b7:5c:b4:88:ca:37:6e:8b: ce:f9:96:fd:b4:f5:47:b5:20:77:bb:fc:a8:9d:81: b2:6c:f8:c7:09:6a:dd:22:6e:83:3f:a7:53:df:f1: da:2f:29:6b:22:c3:e9:1d:65:e8:c5:a0:ba:13:4e: 16:3f:03:93:f0:a5:59:8a:1a:80:e8:27:7d:49:23: df:d1:f9:4b:97:b7:01:c4:19:f5:f1:c5:ff:91:33: d0:a1:74:c6:ee:d4:cf:f6:38:0c:ed:bd:5e:aa:44: fb:88:f7:7b:99:70:76:34:55:7e:55:d2:0f:9e:bf: 94:93 ... (中间省略) Signature Algorithm: sha256WithRSAEncryption 63:21:07:23:47:06:eb:b3:7c:77:6c:df:bc:55:12:b9:f1:5e: 6a:04:60:16:be:d0:0b:18:9c:94:0c:a8:82:08:25:0d:26:fb: dd:cb:fc:8c:27:d9:0c:fa:4a:b6:31:b6:67:f0:26:2c:0d:96: 96:39:65:3f:d9:a1:ee:de:9c:10:4d:54:e1:c8:d6:a9:0e:77: db:00:e2:37:e3:3f:b4:9c:31:4f:ac:74:d3:22:12:53:36:d0: ef:18:07:2d:8e:d0:e6:91:b2:6c:4a:5e:39:53:14:58:4e:d1: 50:04:c9:83:7e:0d:7b:15:96:87:11:d7:5d:4a:17:ac:aa:9f: 84:e3:a8:24:9d:d6:17:77:26:8c:9f:7a:7b:18:da:39:2f:77: f7:2b:c7:23:b8:97:6f:c3:d1:72:4c:7e:fc:c6:0d:cc:73:38: 19:81:fb:e7:c1:7a:e8:b9:1d:3a:05:dc:36:04:9b:f1:f0:e1: a6:47:a0:30:4f:55:90:6c:da:cf:9e:b2:76:12:11:a1:5c:b6: 61:8d:15:a4:68:65:9a:57:2f:7a:6e:a3:1f:f5:b4:92:5a:3c: df:71:0a:cd:57:d4:d0:15:36:7e:ba:d5:03:25:27:45:b4:60: cd:2e:02:c1:0f:0a:e7:41:6f:58:69:20:9e:ad:47:52:1a:b5: e6:e5:8d:1d
可以看到,除了上面 Chrome 里显示的一些基本信息外,证书里还包含了几个重要信息:
- 服务器端的公钥,即上面 RSA Public-Key 下面的那一长串数字,就是百度的公钥了。
- 签名,证明数字证书有效的关键信息。如果把数字证书类比成一张合同的话,我们知道合同需要老板签字才算有效,同样,数字证书是需要 CA 签名才算有效的,这里的一长串字符就是 CA 对该证书的“签名”了。
那么,又有问题来了:我们浏览器是从哪里拿到 CA 的公钥呢?总不能还是通过网络传输吧,这样就有“套娃”的中间人攻击风险了。所以啊,我们的浏览器或操作系统已经内置了世界权威的 CA 的数字证书(证书里就包含了其公钥)了,点击浏览器的 设置 -> 隐私设置和安全性 -> 安全 -> 管理设备证书,可以查看当前系统内置的所有 CA 证书。


- 客户端向服务器发送 Client Hello 信息,告知自己想要建立一条 TLS 连接,并告知自己支持的加密算法。
- 服务器向客户端发送一个 Server Hello 的回应,并选择一个加密算法,同时给客户端发送自己的数字证书(包含服务器的公钥)。
- 客户端验证服务器发来的数字证书,验证通过后,在心里默默想出一个 pre-master 密钥(预主密钥),然后使用服务器的公钥,将预主密钥进行加密后,发送给服务器。
- 服务器用自己的私钥进行解密,得到预主密钥。
- 客户端和服务器都通过预主密钥,进行相同的计算后,得到后续通信时使用的对称加密密钥,称为 shared secret。
- 客户端和服务器端都分别用生成的 shared-secret 加密一段报文后,发送给对方,以验证对方能够成功收到信息并解密。
然后 TLS 就建立成功了,接下来双方都用这个 shared-secret 进行加密通信。
到这里,HTTPS 的原理就已经全部介绍完毕了。如果还有什么疑问,欢迎私信我或在留言区讨论。
- 数据传输过程中的安全问题,因为它对数据进行了加密,只有浏览器和服务器可以对其进行解密。
- 浏览器对服务器的信任问题,数字证书以及其中的数字签名,保证了我们访问的就是我们想要访问的服务器,不可能被钓鱼网站欺骗,也不可能被中间人攻击所欺骗。
当然,保证以上安全的前提是我们的电脑本身没有被攻破,如果你的电脑被黑客攻击,装上了来历不明的根证书,那么 HTTPS 也不能保障你的安全了。

点击 Request Headers 后面的 View source,可以看到原始的 HTTP 请求报文,摘抄如下
GET / HTTP/1.1 Accept: text/html,... Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9 Cache-Control: no-cache Connection: keep-alive Host: httpbin.org Pragma: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/112.0.0.0 Safari/537.36
其中:
- GET 表示请求方法,常见的 HTTP 请求方法有 GET、POST、PUT、DELETE 等…
- GET 后面的 /表示请求路径,这里我们访问的根路径,所以显示为 /。如果你访问 httpbin.org/get的话,这里显示的就是 /get了
- HTTP/1.1 表示使用的 HTTP 协议版本,现在常用的有 HTTP/1.1 和 HTTP/2,当然还有更先进的 HTTP/3,这里就不过多展开了
- 下面的 9 行全部都是 HTTP header,每一个 header 包含 name 和 value,之间用冒号分隔开。
HTTP/1.1 200 OK Date: Sat, 08 Apr 2023 16:28:43 GMT Content-Type: text/html; charset=utf-8 Content-Length: 9593 Connection: keep-alive Server: gunicorn/19.9.0 Access-Control-Allow-Origin: * Access-Control-Allow-Credentials: true body...
HTTP/1.1 指的协议版本,响应和请求的版本肯定是一样的了
- 200 OK 代表返回的响应码,表示这个响应是 OK 的。另外还有非常常见的返回码 404 NOT FOUND ,表示服务器告诉你你访问的这个资源不存在…
- 后面 7 行全部是 HTTP header,同样每一个 header 包含 name 和 value,之间用冒号分隔开。
- 最后是 HTTP Body,也就是响应体,在 Response Header 里是看不到了,不过点击上面 Tab 页的 Response 后,就可以看到完整的响应体了。
由于本篇文章的重点不在 HTTP,这里就不再过多展开,非常建议大家平时使用浏览器访问网页的时候,有事没事多打开下上面的开发者工具,看看 HTTP 到底是怎么工作的,说不定,还能找到一份工作呢…

curl -v httpbin.org
可以看到整个 HTTP 交互的流程

- 首先利用 openssl工具,模拟一个证书权威机构(CA),生成 CA 证书以及密钥。
- 然后利用 openssl工具,模拟 techcorner.cn网站的维护者,向上面的 CA 机构申请一个数字证书
- 使用 Nginx 搭建自己的 HTTPS 服务器,并使用上面生成的服务器证书。
- 使用浏览器访问自己搭建的 HTTPS 服务器,体会全加密流程。
- Mac / Linux 服务器
- 安装 openssl 工具、以及 Nginx 。
# 生成 CA 私钥
openssl genrsa -out ca.key 2048
然后生成证书签发申请文件
# 生成证书签发申请文件(.csr)
openssl req -new -key ca.key -out ca.csr -subj '/C=CN/ST=Zhejiang/L=Hangzhou/O=Tech_Corner/CN=ROOTCA'
# 自签一个根证书
openssl x509 -req -days 365 -sha256 -extensions v3_ca -signkey ca.key -in ca.csr -out ca.cer
- ca.key:CA 私钥
- ca.csr:CA 为自己签发证书的请求。(该文件的作用是生成 CA 证书,生成之后不会再用到)
- ca.cer:CA 为自己签发的证书
$ openssl x509 -in ca.cer -text -noout Certificate: Data: Version: 1 (0x0) Serial Number: 92:63:8f:e3:23:97:e1:c6 Signature Algorithm: sha256WithRSAEncryption Issuer: C=CN, ST=Zhejiang, L=Hangzhou, O=Tech_Corner, CN=ROOTCA Validity Not Before: Apr 11 15:39:56 2023 GMT Not After : Apr 10 15:39:56 2024 GMT Subject: C=CN, ST=Zhejiang, L=Hangzhou, O=Tech_Corner, CN=ROOTCA ... 以下省略
2. 生成服务器证书
# 生成 CA 私钥
openssl genrsa -out server.key 2048
openssl req -new -key server.key -out server.csr -subj '/C=CN/ST=Zhejiang/L=Hangzhou/O=Tech_Corner/CN=techcorner.cn'
openssl x509 -req -extfile <(printf "subjectAltName=DNS:techcorner.cn,DNS:www.techcorner.cn") -days 365 -in server.csr -CA ca.cer -CAkey ca.key -CAcreateserial -out server.cer
$ openssl x509 -in server.cer -text -noout Certificate: Data: Version: 1 (0x0) Serial Number: 80:56:02:4d:21:ff:5e:60 Signature Algorithm: sha256WithRSAEncryption Issuer: C=CN, ST=Zhejiang, L=Hangzhou, O=Tech_Corner, CN=ROOTCA Validity Not Before: Apr 11 15:52:10 2023 GMT Not After : Apr 10 15:52:10 2024 GMT Subject: C=CN, ST=Zhejiang, L=Hangzhou, O=Tech_Corner, CN=techcorner.cn ...以下省略
上面可以看到该证书的基本信息(Subject),以及颁发者信息(Issuer)等。
server { listen 443 ssl; server_name techcorner.cn; ssl_certificate /path/to/server.cer; ssl_certificate_key /path/to/server.key; ssl_session_cache shared:SSL:1m; ssl_session_timeout 5m; ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; location / { root html; index index.html index.htm; } }
- ssl_certificate的值修改为上一步生成的 server.cer 的绝对地址
- ssl_certificate_key 的值修改为上一步生成的 server.key 的绝对地址
然后启动 Nginx

如果想要正常访问,可以在系统的根证书列表中,将我们自己模拟的 CA 证书添加进去(Mac 系统中,将根证书 ca.cer 拖入钥匙串访问的 系统 中即可,并对其进行 “始终信任”)。


127.0.0.1 techcorner.cn


微信赞赏
支付宝扫码领红包