HTTPS 不是另一个协议,就是 HTTP 加三样东西:加密(防被读)、认证(防冒充)、 完整性(防被改)。三样都由 TLS 提供,HTTP 的报文格式一个字节都没改。
7.1 的三节各说了一种风险,这一节把它们逐一对应到 TLS 的三项保护上。
一、三样东西各管一件事
对照 7.1 的三节,正好一一对应: | 风险 | TLS 的手段 | 少了它会怎样 | |---|---|---| | 被窃听 | 记录加密 | 机票密码明文躺在链路上 | | 被冒充 | 证书链加主机名校验 | 连上的是中间人 | | 被篡改 | AEAD 认证标签 | 转账金额被改一个字 | 注意这三项不是一个开关。 只加密不验证证书,等于跟陌生人用密语聊天——内容别人听不懂,但对面可能是骗子。 反过来只验证不加密,身份是真的,内容却是敞开的。三样得同时成立。 这也是为什么「跳过证书警告继续访问」特别危险:它把「认证」这一项关掉了, 剩下的加密和完整性就都变成了给中间人准备的。
二、代价能量出来
Demo 里同一台服务端跑两遍。 明文那一组:请求 36 字节、响应 126 字节,合计 162 字节全在网线上裸奔。 加 TLS 之后,用一层透明中转把经过的字节数记下来:握手阶段上行 2456、下行 1597, 合计 4053 字节,耗时 25.9 毫秒。 4053 对 162——握手一次的开销比整条明文报文大二十多倍。 这就是「HTTPS 慢」这个印象的真实来源:慢在握手,不在传输。 握手之后再看传输:整轮共 4769 字节,减去握手 4053,数据部分 716 字节,而明文那条只要 162。 多出来的约 550 字节是TLS 记录头、认证标签、以及双方各发一次的 Finished——每一条记录都要摊这份钱。
三、握手里最贵的那一块
再量一次,强制只用经典曲线 X25519:握手降到 1987 字节,
差 2066 字节——超过一半的握手流量花在密钥交换上。
原因是 OpenSSL 3.5 默认带 X25519MLKEM768 这类后量子混合密钥交换,双方的份额都比传统曲线大得多。
多出来的两千字节就是「抗量子」的当下成本。
换来的是:「今天把密文录下来,等以后有量子计算机再解」这条路走不通。
至于值不值,取决于你的数据要保密多少年——这也是密码学里少见的能直接用字节数算清楚的权衡。
顺带说一句:https 的默认端口是 443,http 是 80。
URL 里换了 scheme,底层其实是「同一个端口号上跑不跑 TLS」的区别。
四、动手 Demo:同一台服务端,明文与 TLS 各跑一遍
先用明文跑一次,量请求和响应的字节; 再给同一台服务端套上自签证书的 TLS,中间夹一层只数字节的透明中转,分别量握手段和数据段。 最后把密钥交换算法换掉再量一次,看那部分占多少。
with tls_server(cert, key) as tp:
with counter_proxy(tp) as (cp, cnt):
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ctx.load_verify_locations(cert)
s = socket.create_connection(('127.0.0.1', cp), timeout=5)
with ctx.wrap_socket(s, server_hostname=u'local.test') as t:
after_hs = dict(cnt) # 握手一结束就记一笔
t.sendall(X.req(u'GET', u'/', u'local.test'))
... # 收完响应再看总账
# 换掉密钥交换再量一次
ctx2.set_ecdh_curve(u'X25519') # 关掉后量子混合
实跑输出:
--------------------------------------------------------------------
三项保护,一次加齐
--------------------------------------------------------------------
先看清楚 HTTPS 到底多了什么。明文那一组:
请求 36 字节,响应 126 字节,合计 162 字节全在网线上裸奔
抓包看到的头一行:GET / HTTP/1.1
同一台服务端加上 TLS 之后,用一层透明中转把经过的字节数记下来:
握手阶段:上行 2456 字节、下行 1597 字节,合计 4053 字节,耗时 25.87 毫秒
握手之后发同一请求:全程共 4769 字节,其中数据部分 716 字节
收到的正文 27 字节,解出来是:这一页讲的是 HTTPS。
中转那头看到的是密文,头一行根本不是 `GET / HTTP/1.1`——
它只能看见 TLS 记录的类型和长度。这就是「外壳」两个字的字面意思。
三项保护各对应前面三节说的一种风险:
机密性 ← TLS 记录加密,中转那头读不出正文(对应 7.1.1)
身份 ← 证书链加主机名校验,冒充者过不了这两关(对应 7.1.2)
完整性 ← AEAD 的认证标签,改一个字节解密就失败(对应 7.1.3)
代价也能量出来:握手 4053 字节,比整条明文请求加响应(162 字节)还大。
所以短连接上 HTTPS 的开销主要在握手,长连接和会话复用才划算。
握手这 4053 字节里,最贵的一块是密钥交换。再量一次,强制只用经典曲线:
默认(含后量子混合) 4053 字节
只用 X25519 1987 字节
差 2066 字节
OpenSSL 3.5 默认带 X25519MLKEM768 这类混合密钥交换,
多出来的一两千字节就是「抗量子」的当下成本——换来的是
「今天录下密文、等以后有量子计算机再解」这条路走不通。
明文:请求 36 字节、响应 126 字节,合计 162 字节。
TLS:握手上行 2456、下行 1597,合计 4053 字节,耗时 25.9 毫秒;
整轮 4769 字节,数据段 716 字节,正文 27 字节。
密钥交换换成纯 X25519 后握手只剩 1987 字节,
差 2066 字节——超过一半的握手流量花在了后量子混合份额上。
中转那头只看得到密文,头一行根本不是 GET / HTTP/1.1。
它只知道「有 1597 字节要往下走」,不知道里面是什么。
30 秒实验
- 把
set_ecdh_curve换成prime256v1,看握手字节和 X25519 差多少。 - 在 TLS 连接上连发三个请求,量总字节,看握手占比降到多少。
- 想一想:如果只加密不验证证书,攻击者能做什么?
常见误区
- 「HTTPS 是另一个协议」——报文格式没变,只是套了层 TLS。
- 「HTTPS 慢在传输」——慢在握手,数据段的额外开销是每记录几十字节。
- 「跳过证书警告只是个小风险」——它关掉的正是「认证」这一项。
自测三问(点开对答案)
1. HTTPS 比 HTTP 多了哪三样东西? —— 加密、认证、完整性保护,都由 TLS 提供。 2. 为什么只加密不验证证书没意义? —— 内容别人听不懂,但对端可能是中间人,加密等于白加。 3. HTTPS 的主要开销在哪一段? —— 握手。实测 4053 字节,比整条明文请求加响应还大二十多倍。三样东西都懂了。接下来把它剥开看:那层「外壳」到底套在哪一层。
- ☐ (本节完,下一节 7.2.2 HTTPS是身披SSL外壳的HTTP)