为什么这道面试题值得单独梳理
“在浏览器输入一个 HTTPS 地址,直到页面显示出来,发生了什么?”是面试中经常遇到的问题。
难点在于:背得出 DNS、TCP、TLS,却容易在追问时混淆它们各自解决的问题。例如,TCP 已经建立了连接,为什么还要 TLS 握手?既然有了证书,为什么还要交换临时公钥?
这篇文章用一个地址贯穿全程:https://www.example.com/index.html。先建立能复述的主线,再展开 TLS,最后用追问检查理解。
约定场景:需要访问网络、没有可复用连接,使用 HTTP/1.1 或 HTTP/2,并进行基于证书和 ECDHE 的 TLS 1.3 完整握手。 缓存、连接复用和 HTTP/3 会改变这条路径,后文分别说明。
严格地说,“一次 HTTPS 请求”关注请求与响应;“输入地址到页面显示”还包含浏览器渲染。面试时先判断题目问到哪一层。
先记住一条主线:找、连、验、传、画
把一次访问想象成与一家机构办理业务:先找到地址,接通线路,确认对方身份并约定保密方式,再交换材料,最后查看结果。
| 记忆词 | 对应阶段 | 解决的问题 |
|---|---|---|
| 找 | URL 解析、缓存检查、DNS | 要访问什么?能否直接用缓存?对方在哪里? |
| 连 | TCP 三次握手 | 如何建立可靠、有序的字节流? |
| 验 | TLS 握手 | 对方是谁?如何获得加密通信所需的密钥? |
| 传 | HTTP 请求、服务端处理、HTTP 响应 | 要办什么业务?处理结果是什么? |
| 画 | 浏览器解析与渲染 | 如何把收到的内容变成页面? |
“验”包含身份认证、密钥协商和握手完整性校验,不能只记成“检查证书”。
URL 与缓存检查 ↓ 需要访问网络,且没有可复用连接DNS:域名 → IP 地址 ↓TCP:建立连接 ↓TLS:认证身份、协商密钥 ↓HTTP:发送请求 → 服务端处理 → 返回响应 ↓ 响应是 HTML浏览器:解析 → 布局 → 绘制 → 合成再记住三层职责:TCP 管可靠传输,TLS 管通信安全,HTTP 管请求与响应的含义。
对于本文的 TCP 场景,发送数据时可以理解为 HTTP → TLS → TCP → IP,接收时反向处理。DNS 是建立连接前可能需要的查询过程。
第 1~3 步:找到服务器,建立 TCP 连接
1. URL 解析与缓存:先判断是否需要出门
对于 https://www.example.com:443/index.html?name=tom#intro:
| 部分 | 含义 |
|---|---|
https | 使用 HTTPS |
www.example.com | 目标域名 |
443 | 端口;HTTPS 省略端口时默认使用 443 |
/index.html | 请求路径 |
name=tom | 查询参数,会随请求发送 |
intro | 片段标识,通常用于页面内定位,不随 HTTP 请求发送 |
浏览器还会根据请求和缓存策略决定是否使用缓存。理解两种情况即可:
- 缓存可直接复用:例如缓存仍然新鲜,且策略允许使用,可以直接取得内容。
- 缓存需要验证:例如携带
If-None-Match向服务器验证 ETag;服务器返回304 Not Modified时,浏览器复用已有响应体。这仍然发生了网络请求。
因此,“有缓存”不等于“一定不联网”。缓存复用与验证的规则见 RFC 9111 第 4 节。
如果页面受 Service Worker 控制,请求也可能被它拦截。浏览器还会检查是否有合适的现成连接。这里描述的是逻辑上的判断,实际实现可能并行或提前执行部分工作。
2. DNS:把域名转换成可以连接的地址
DNS 的主要任务是回答:“www.example.com 对应哪些 IP 地址?”
浏览器或操作系统可能已有解析缓存;缓存未命中时,查询配置的递归解析器。解析器也有缓存,必要时才继续向其他 DNS 服务器查询。
浏览器 / 操作系统 ↓ 查询递归解析器 ↓ 缓存不足时,按委派关系逐级查询根 DNS → 顶级域 DNS(如 .com)→ 权威 DNS ↓解析结果返回客户端根 DNS 通常提供下一步该问谁的信息,不直接给出网站最终 IP。逐级查询通常由递归解析器完成,也不是每次访问都要从根开始。
DNS 可能返回多个地址;如果使用 CDN,客户端连接的可能是边缘节点。后文的“服务器”首先指当前连接的对端,不一定是最终处理业务的源站。
记忆点:DNS 找地址,不负责证明地址背后的服务器可信。
3. TCP:同步双方的序列号,建立可靠字节流
得到地址后,客户端向目标端口发起 TCP 连接。普通三次握手可以写成:
客户端 服务器 | ---- SYN,seq = x --------------------> | | <--- SYN + ACK,seq = y,ack = x+1 ---- | | ---- ACK,ack = y+1 ------------------> |SYN 用于同步序列号,ACK 表示确认。双方各有自己的初始序列号,第三次握手让服务器确认客户端收到了自己的初始序列号。
为什么不能简单地只握手两次?如果服务器收到一个延迟到达的旧 SYN,仅凭它和自己的回复,不能确认客户端此刻确实要建立这条连接。还需要客户端对本次服务器序列号作出有效确认。防止旧的重复连接请求造成混淆,是三次握手的重要目的。参见 RFC 9293 第 3.5 节。
连接建立后,TCP 通过序列号、确认、重传等机制提供可靠、有序的字节流,但它不验证网站身份,也不加密内容。这正是下一步需要 TLS 的原因。
第 4 步:TLS 如何让这条连接可信、保密
TLS 要提供三项保障:身份认证、机密性、完整性。分别回答“我在和谁通信”“别人能否看懂”“内容被改动后能否发现”。
理解握手前,先解决最常见的混淆:证书里的密钥、交换时用的密钥、传输数据时用的密钥,分别是什么?
三类密钥,各司其职
| 密钥 | 来源与寿命 | 在本文握手中的用途 |
|---|---|---|
| 证书身份密钥对 | 服务器持有私钥,公钥放在证书里,通常长期使用 | 服务器签名,客户端验签,证明身份 |
| ECDHE 临时密钥对 | 双方为本次握手分别生成 | 交换公钥份额,各自计算共享秘密 |
| 对称流量密钥 | 根据共享秘密等材料派生 | 加密并认证握手消息或 HTTP 数据 |
可以记成:身份密钥证明“我是谁”,临时密钥帮助“算秘密”,流量密钥负责“护数据”。
公钥可以公开,私钥留在本地。最终的数据加密密钥也不会作为明文在网络上传送。
TLS 1.3 的这类握手不会用证书中的 RSA 公钥加密并传送会话密钥。RSA 仍可用于身份签名,要把“RSA 签名”和旧式“RSA 密钥交换”分开理解。
先看握手全貌
以下省略客户端证书认证、会话恢复及握手重试,展示普通 TLS 1.3 完整握手:
客户端 服务器 | ---- ClientHello --------------------> | | 支持的参数 + 临时公钥份额 A | | | | <--- ServerHello --------------------- | | 选定的参数 + 临时公钥份额 B | | | | 双方各自计算共享秘密,派生握手密钥 | | | | <--- EncryptedExtensions ------------- | | <--- Certificate --------------------- | | <--- CertificateVerify --------------- | | <--- Finished ------------------------ | | 以上四项使用握手密钥加密 | | | | 验证证书、身份签名和服务器 Finished | | ---- 客户端 Finished ----------------> | | ---- 加密的 HTTP 请求 ----------------> | | <--- 加密的 HTTP 响应 ----------------- |图中的多条消息不代表多次独立往返,服务器通常可连续发送自己的这一组消息。协议时序可对照 RFC 8446 第 2 节。
Hello:交换能力与临时公钥份额
ClientHello 可以理解为:“这是我支持的配置,以及本次协商用的公开材料。”主要包含:
- TLS 版本、加密套件、支持的密钥交换组和签名算法等能力。
- 客户端随机数,以及
key_share中的临时公钥份额。 - SNI:告诉服务器要访问哪个域名,便于同一 IP 上的网站选择证书。
- ALPN:提出支持的应用层协议,例如
h2和http/1.1。
服务器通过 ServerHello 选择 TLS 参数,并返回自己的临时公钥份额。在 TLS 1.3 中,ALPN 的选择结果放在后续加密的 EncryptedExtensions 中。
这里还要区分:SNI 是帮助服务器选择网站的提示,证书域名校验才用于检查身份是否匹配。
ECDHE:交换公开材料,各自算出相同秘密
ECDHE 中的 E 表示临时性(Ephemeral)。为了理解其关系,可以用椭圆曲线上的简化记法:
公开约定:曲线及基点 G
客户端:保留私钥 a,发送公钥 A = aG服务器:保留私钥 b,发送公钥 B = bG
客户端计算:aB = a(bG)服务器计算:bA = b(aG)
两边得到同一个结果:abG这里是椭圆曲线上的运算关系,不是普通整数乘法,也不是可以直接照抄的密码实现。实际协议按所选算法从结果中取得共享秘密。
旁观者能看到 G、A、B,却在安全参数下难以计算出私钥或共享秘密。关键在于:双方传送的是公开材料,秘密由双方各自在本地计算出来。
但这一步还没证明身份。中间人可以尝试与两端分别协商两套秘密,因此还需要证书和签名把本次握手绑定到目标服务器。
HKDF:把共享秘密派生成不同用途的密钥
ECDHE 得到的共享秘密不会直接拿来当 AES 密钥。TLS 1.3 使用 HKDF 进行密钥派生,并结合握手消息摘要等上下文。
共享秘密等材料 + 对应阶段的握手摘要 ↓ HKDF 分阶段派生 ┌─────────┴─────────┐ ↓ ↓握手阶段所用密钥 应用数据所用密钥 ↓ ↓分别区分客户端、服务器的发送方向所以,“双方协商出一把会话密钥”只是入门简写。准确理解是:双方得到一致的基础秘密,再派生出按用途、按方向区分的密钥。客户端发送用的密钥,对应服务器接收时使用的密钥。
交换完 ServerHello 后,双方已经能派生握手密钥,所以证书等后续握手消息可以加密传送。能够加密通信,此时还不等于已经确认了对方身份。
证书、CertificateVerify、Finished:三项检查不能互相替代
这三项分别回答不同的问题:
| 检查项 | 核心问题 | 验证依据 |
|---|---|---|
| Certificate:证书 | 这个公钥能否被信任为目标域名的身份公钥? | 信任链、域名、有效期、用途等 |
| CertificateVerify:身份证明 | 对方是否持有对应私钥,并认可本次握手内容? | 用证书公钥验证本次握手的签名 |
| Finished:握手校验 | 密钥材料是否匹配,握手记录是否一致? | 验证基于派生密钥和握手摘要的 MAC |
第一项:证书为什么可信?
典型证书链是:本地信任的根 CA → 中间 CA → 网站证书。客户端验证链上的签名和约束,检查证书有效期、用途,以及 SAN 中的域名是否与目标域名匹配,并按客户端策略处理撤销等检查。
信任的起点来自浏览器或操作系统的信任库。服务器随便发来一张自签名根证书,并不会因此自动获得信任。域名身份匹配规则见 RFC 9525 第 6 节。
第二项:为什么发了证书还要签名?
证书是公开材料,任何人都可能复制。服务器还要用证书对应的私钥,对包含本次握手摘要的规定内容签名,这就是 CertificateVerify。
客户端用证书公钥验签,才能确认对方持有私钥,并将此前交换的关键握手参数绑定到这个身份。攻击者只复制证书或替换临时公钥,都无法凭空生成有效签名。
第三项:Finished 为什么不是一句“结束了”?
Finished 携带基于专用派生密钥和握手摘要计算的消息认证码(MAC)。接收方根据自己的握手记录和密钥重新计算、比较,验证双方密钥材料与握手记录是否匹配;验证失败就终止握手。
它使用共享秘密派生出的密钥进行校验,与 CertificateVerify 使用身份私钥进行数字签名是两种机制。细节见 RFC 8446 的认证消息定义。
客户端完成服务器验证后,发送自己的 Finished,随后即可发送正常应用数据,不需要额外等一句“收到 Finished”。
把 TLS 这一段压缩成一句话:Hello 谈参数,ECDHE 算秘密,HKDF 派密钥,证书与签名验身份,Finished 对记录,AEAD 护数据。 这句概括职责;实际顺序以时序图为准。
第 5~8 步:传送 HTTP 请求、处理业务、展示页面
5. 发送请求:HTTP 表达业务,TLS 保护数据
TLS 准备好后,浏览器可以发送请求。下面用 HTTP/1.1 文本形式帮助理解:
GET /index.html HTTP/1.1Host: www.example.comAccept: text/html方法和路径表达“想做什么”,请求头携带附加信息;请求也可以按需要携带请求体。HTTP/2 保留这些语义,但使用二进制帧编码,并非直接发送上述文本。
对 HTTP 数据的保护过程可以简化成:
HTTP 数据 → TLS 记录层进行 AEAD 加密 → TCP 传输AEAD 是认证加密,例如 AES-GCM、ChaCha20-Poly1305:既保护内容机密性,也用认证标签检查完整性。接收方解密并校验成功后,才把数据交给上层。
这也是 TLS 不用非对称密码直接加密全部 HTTP 数据的原因:公钥机制适合协商秘密和认证身份,对称认证加密适合高效处理大量数据。
6. 服务端处理:先分清 TLS 在哪里终止
一种可能的部署结构是:
浏览器 → CDN / 反向代理 → 应用服务 → 缓存 / 数据库 ↓ ↓ 可能直接返回 执行业务并生成响应这些节点并非必然全部存在。静态资源可能直接由 CDN 返回,也可能根本不需要访问数据库。
如果 TLS 在 CDN 或反向代理处终止,这个节点就能解密浏览器发来的请求。它到源站的连接是另一段链路,是否使用 TLS 取决于部署配置。因此,浏览器到入口的 HTTPS 并不自动说明所有内部链路都已加密。
7. 返回响应:得到状态、元信息和内容
响应包含状态码、响应头,以及适用时的响应体。例如 200 表示请求成功,Content-Type 表示内容类型,响应体可以是 HTML。
响应沿反方向传回,浏览器通过 TLS 解密和校验后交给 HTTP 层处理。若返回重定向,可能继续发起新请求;若是用于缓存验证的 304,则复用已有响应体。
一次请求结束不代表连接结束。 同一条连接可以继续承载后续请求;HTTP/2 还支持在连接内使用多个流并发传送请求和响应。
8. 渲染页面:从内容到像素
当响应是 HTML 时,浏览器的渲染过程可按下面的依赖关系理解:
HTML → DOM ─────┐ ├→ 渲染树 → 布局 → 绘制 → 合成CSS → CSSOM ───┘- DOM 表示文档结构,CSSOM 表示样式规则。
- 渲染树 描述需要呈现的内容及样式,布局计算位置和尺寸。
- 绘制 产生绘制指令,后续光栅化和合成等工作把内容呈现到屏幕。
实际加载不是等所有内容到齐后才依次开始:浏览器可以边接收边解析,并继续请求 CSS、JavaScript、图片和字体。脚本执行可能阻塞解析或修改 DOM、样式,从而触发后续渲染工作。关于渲染树、布局和绘制,可参阅 web.dev 的渲染流程说明。
如果请求的是 JSON 接口,到响应交给调用方时,请求流程就已完成;它本身不一定触发页面渲染。
面试追问:把容易背错的地方讲明白
TLS 1.3 的 1-RTT,和 TCP 三次握手是什么关系?
RTT 是数据往返一次的时间。“三次握手”数的是三次报文交互,不是三次往返。
在不考虑丢包、握手重试等情况的简化模型中,客户端收到 SYN + ACK 后可发送第三次确认并开始发送 ClientHello。从客户端开始握手到能够继续发起 TLS,约计 1 RTT。
TLS 1.3 把临时公钥份额提前放进 ClientHello,服务器收到后通常就能完成参数选择,返回自己的公钥份额和认证消息。客户端等待这一轮回复、完成验证并发送 Finished 后,即可发送请求,因此完整 TLS 握手通常按 1 RTT 计,不包含前面的 TCP 时间。
| 阶段 | 简化耗时 |
|---|---|
| DNS | 取决于缓存与查询路径 |
| TCP 建连 | 约 1 RTT |
| TLS 1.3 完整握手 | 通常 1 RTT |
| HTTP 请求到首字节返回 | 约 1 RTT + 服务端处理时间 |
因此可以估算:
首字节时间 ≈ DNS 时间 + 3 × RTT + 服务端处理时间这个估算假设使用全新 TCP 连接、普通 TLS 1.3 完整握手,忽略本地计算等额外开销。它是帮助理解延迟来源的模型,不是所有访问的固定耗时,更不是页面完全显示的时间。TLS 如果需要 HelloRetryRequest 补交合适的密钥份额,还会增加往返。
TLS 1.2 和 TLS 1.3 的区别,记哪些就够了?
| 对比项 | TLS 1.2 | TLS 1.3 |
|---|---|---|
| 普通完整握手耗时,不含 TCP | 通常 2 RTT | 通常 1 RTT |
| 密钥交换 | 支持 RSA、DHE、ECDHE 等 | 支持 (EC)DHE、PSK 及其组合;移除静态 RSA 密钥交换 |
| 对称数据保护 | 支持 AEAD,也包含较旧方案 | 仅使用 AEAD |
| 握手内容保护 | 证书等消息通常明文传送 | ServerHello 之后的握手消息加密 |
TLS 1.2 也能使用 ECDHE 并提供前向保密;TLS 1.3 也能使用 RSA 做签名。 不要把协议版本与某一个算法画等号。协议变更见 RFC 8446 第 1.2 节。
证书私钥泄露后,过去的流量还能解密吗?
在正确使用 ECDHE、临时秘密已安全销毁且其他会话秘密未泄露的前提下,仅拿到长期证书私钥,通常无法解密此前录下的流量。这就是前向保密。
原因是:证书私钥负责身份签名,历史会话的数据密钥来自当时的临时密钥交换。身份私钥泄露依然会威胁后续身份认证;前向保密保护的是符合条件的历史会话。
连接复用、会话恢复、0-RTT 是一回事吗?
| 机制 | 复用了什么 | 对流程的影响 |
|---|---|---|
| 连接复用 | 已经建立好的连接 | 后续请求通常无需重新进行 TCP、TLS 握手 |
| TLS 会话恢复 | 先前握手建立的恢复材料,如关联 Ticket 的 PSK | 新连接仍要握手,但可以简化认证过程 |
| 0-RTT | 已有 PSK 等条件下的提前发送能力 | 在收到本次服务器握手回复前就发送部分应用数据 |
会话恢复不等于一定启用 0-RTT,0-RTT 也不代表没有握手或没有网络延迟。
0-RTT 的关键限制是重放风险:攻击者未必能解密请求,却可能把记录的早期数据再次发给服务器。涉及转账、创建订单等不可安全重复的操作,不能直接假定可以放进 0-RTT。相关约束见 RFC 8446 第 8 节。
HTTP/3 为什么不走 TCP 三次握手?
HTTP 版本与 TLS 版本是两个维度。先分清下面三条协议路径:
| HTTPS 场景 | 协议关系 |
|---|---|
| HTTP/1.1 | HTTP/1.1 → TLS → TCP |
| HTTP/2 | HTTP/2 → TLS → TCP |
| HTTP/3 | HTTP/3 → QUIC → UDP;QUIC 集成 TLS 1.3 握手 |
HTTP/3 把 HTTP 语义映射到 QUIC,连接建立和安全握手由 QUIC 配合 TLS 1.3 完成,因此没有“先 TCP、再 TLS”这一步骤。UDP 本身不保证可靠传输,所需的可靠性等能力由 QUIC 提供。参见 RFC 9114。
HTTP/2 虽然可以多路复用,但底层 TCP 丢失一段数据时,有序交付会让同一连接上的多个 HTTP 流一起等待。QUIC 使用独立的流交付机制,可以避免这种跨流的 TCP 队头阻塞;丢包仍可能影响对应流,拥塞控制也仍会影响整体传输。
HTTPS 会隐藏所有信息吗?
HTTP 路径、查询参数、请求头和请求体都在 TLS 的保护范围内,但源与目的 IP、流量大小和时间等网络元数据仍可能被观察到。在没有 ECH 等额外保护的普通握手中,SNI 也可能暴露访问域名。
还要记住 TLS 的保护边界:通信端点能够看到解密后的内容,HTTPS 也不代表网站的业务本身一定可信。
一分钟回答:先交代前提,再沿主线展开
下面这段适合在面试中作为第一轮回答,后续再按追问展开 TLS 或浏览器渲染:
我先以没有可用缓存和连接复用、使用 HTTP/2 与 TLS 1.3 的情况说明。浏览器解析 URL,通过 DNS 得到目标地址,再通过 TCP 三次握手建立连接。随后进行 TLS 握手:协商参数,通过 ECDHE 计算共享秘密并派生密钥,验证服务器证书、握手签名和 Finished。浏览器在安全连接上发送 HTTP 请求,服务端处理后返回响应。如果响应是 HTML,浏览器继续加载所需资源,解析 HTML 和 CSS,完成布局、绘制与合成。连接可以继续复用。如果缓存能直接满足请求,或者改用基于 QUIC 的 HTTP/3,流程会有所不同。
回答时抓住三个层次:先说适用场景,再说完整主线,最后点出会改变流程的分支。 不必一开始就罗列全部扩展字段。
合上文章,用六道题检查是否真正记住
先尝试口头回答,再展开核对。
1. TCP 已经建立连接,为什么还需要 TLS?
TCP 提供可靠、有序的字节流;TLS 在其上提供身份认证、机密性和完整性。两者解决的问题不同。
2. ECDHE 把会话密钥发给对方了吗?
没有。双方交换临时公钥份额,保留各自私钥,在本地计算相同的共享秘密,再通过 HKDF 派生流量密钥。
3. 为什么复制一份合法证书,仍然无法完成冒充?
证书是公开材料。攻击者还需要对应私钥,才能对本次握手生成合法的 CertificateVerify 签名;客户端也会检查证书是否匹配目标域名。
4. 证书公钥、ECDHE 公钥、对称流量密钥分别做什么?
证书公钥用于验证身份签名;ECDHE 公钥参与共享秘密计算;对称流量密钥用于加密和认证握手消息或应用数据。
5. 为什么后续请求不一定再经历 DNS、TCP、TLS 全流程?
解析结果可能已缓存,HTTP 内容可能可直接从缓存取得,连接也可能仍然存在并可复用。请求次数、连接次数和完整握手次数不是一一对应的。
6. HTTP/3 用 UDP,可靠性和安全性由谁提供?
QUIC 在 UDP 之上提供流、丢包恢复等传输能力,并集成 TLS 1.3 握手来建立安全连接。它不需要 TCP 三次握手。
复习时先从空白纸写出“找、连、验、传、画”,再在“验”下面补出三类密钥和三项校验。能解释清楚每一步为什么存在,就能沿着这条主线接住大部分追问。