TLS 夹在应用层和传输层之间:HTTP 报文整段塞进 TLS 记录当载荷,TLS 再交给 TCP。 所以「身披外壳」是字面意思——里层一个字都没改。
上一节说 HTTPS 加了三样东西,这一节说这三样东西加在哪一层。 位置决定了它对上层协议是透明的。
一、位置:在 HTTP 和 TCP 之间
OSI 那一套在这儿只要记一件事:TLS 不是传输层协议,它跑在 TCP 之上、应用协议之下。
对 TCP 来说它是普通数据流,对 HTTP 来说它是可靠的双向字节管道。
正因为这个位置,它对上层是透明的:SMTP、FTP、WebSocket 都能套,不需要各自改协议。
你看到的 https、smtps、ftps 都是同一个套法。
也正因为这个位置,它对下层是不透明的:中间设备只能看到「一段加密数据」,看不到方法、URI、首部。
这既是隐私保护,也是某些网络管理的麻烦来源。
二、实测一次真握手
Demo 真连 www.example.com:443,谈成的是 TLSv1.3,套件 TLS_AES_256_GCM_SHA384,
ALPN 协商成 http/1.1。
证书主题是 CN=example.com,SAN 里有 example.com 和 *.example.com,
有效期到 2026 年 12 月 25 日。
握手字节合计 6698(上行 5085、下行 1617),耗时 435 毫秒;
之后发 41 字节的 GET /,收回 937 字节的明文响应——解开之后头一行还是 HTTP/1.1 200 OK。
三层结构一目了然:中转那头只数得到密文字节,HTTP 报文只有两端解密之后才看得见,
正文和明文 HTTP 里没有任何区别。
三、ALPN 决定了壳里装什么
ALPN 是握手时顺带谈的一件事:「我支持的协议有这些」,服务端挑一个。
所以同一个 443 端口上,可能跑 HTTP/1.1,也可能跑 HTTP/2。
Demo 里故意只报 http/1.1,因为报 h2 之后服务端回的就是二进制帧,
用文本方式看会是一堆乱码——那不是出错,是我们拿 HTTP/1.1 的解析器去读 HTTP/2。
这也解释了为什么 HTTP/2 事实上总跑在 TLS 上:一是浏览器只支持加密的 h2,
二是加密之后中间设备无法按自己的理解改写报文,协议升级才不会被老旧设备搅乱。
四、动手 Demo:真连一次,把三层拆开看
通过一层透明中转连 www.example.com:443,打印协议版本、套件、ALPN、握手字节和证书内容;
然后在同一条连接上发一个 HTTP/1.1 请求,看解密之后的报文长什么样。
with counter_proxy(443, u'www.example.com') as (cp, cnt):
ctx = ssl.create_default_context()
ctx.set_alpn_protocols([u'http/1.1']) # 不要 h2,否则回的是二进制帧
s = socket.create_connection(('127.0.0.1', cp), timeout=8)
with ctx.wrap_socket(s, server_hostname=u'www.example.com') as t:
print(t.version(), t.cipher(), t.selected_alpn_protocol())
print(t.getpeercert()[u'subjectAltName'])
t.sendall(X.req(u'GET', u'/', u'www.example.com'))
实跑输出:
--------------------------------------------------------------------
剥到最里层,还是那套 HTTP
--------------------------------------------------------------------
真的连一次 www.example.com:443:
协议版本 TLSv1.3
加密套件 TLS_AES_256_GCM_SHA384
套件三元组 ('TLS_AES_256_GCM_SHA384', 'TLSv1.3', 256)
ALPN 协商结果 http/1.1
握手耗时 491.3 毫秒
握手字节 上行 5084、下行 1617,合计 6701
证书主题 ((('commonName', 'example.com'),),)
证书里的名字 example.com、*.example.com
生效期 Sep 26 22:49:11 2026 GMT 至 Dec 25 22:56:35 2026 GMT
发出去的请求 41 字节,收回来的响应 937 字节
解密之后头三行:
HTTP/1.1 200 OK
Date: Sun, 25 Oct 2026 06:49:31 GMT
Content-Type: text/html; charset=utf-8
三层结构一目了然:
TLS 记录层 中转那头只数到「握手期间下行 1617 字节的密文」
HTTP 报文 只有两端解密之后才看得见,还是 `GET / HTTP/1.1` 那一套
应用数据 网页正文,跟在明文 HTTP 里没有任何区别
所以「HTTPS 是身披 SSL 外壳的 HTTP」这句话是字面意思:
HTTP 的报文格式一个字节都没改,只是被整段塞进 TLS 记录里当载荷。
这也解释了为什么 HTTP/2 必须跑在 TLS 上——
中间设备看不懂密文,就没法按自己的理解去改写报文了。
协议 TLSv1.3,套件 TLS_AES_256_GCM_SHA384,ALPN 谈成 http/1.1。
握手 6698 字节(上行 5085、下行 1617),耗时 435 毫秒;其中下行那 1617 字节里就有服务端的证书。
证书 SAN 是 example.com 和 *.example.com,有效期到 2026-12-25。
注意 *.example.com 只覆盖一级子域,a.b.example.com 不算。
解密后头一行是 HTTP/1.1 200 OK——和明文 HTTP 一模一样。这就是「身披外壳」的证据:里层根本没变。
30 秒实验
- 把 ALPN 改成
['h2','http/1.1'],看服务端选哪个、响应变成什么样。 - 试着连一个只支持 TLS 1.2 的站点,看套件和握手字节怎么变。
- 想一想:为什么 SMTP 也能套 TLS,而不用另造一个协议?
常见误区
- 「HTTPS 是把 HTTP 改了」——报文格式完全没改。
- 「TLS 属于传输层」——它在 TCP 之上、应用协议之下。
- 「HTTP/2 必须加密是技术限制」——是浏览器和生态的约定。
自测三问(点开对答案)
1. TLS 在协议栈的哪个位置? —— 应用层和传输层之间,跑在 TCP 之上。 2. ALPN 是干什么的? —— 握手时协商壳里装哪个应用协议,比如 http/1.1 或 h2。 3. 为什么说 TLS 对上层透明? —— 任何应用协议都能整段塞进 TLS 记录,不需要各自改造。外壳看清了。接下来钻进壳里,看那两把钥匙是怎么分工的。
- ☐ (本节完,下一节 7.2.3 相互交换密钥的公开密钥加密技术)