HTTPS 如何保证安全?TLS 握手过程是怎样的?
一句话回答
HTTP 明文传输,有窃听、篡改、冒充三个风险。HTTPS 就是 HTTP + TLS:握手时用非对称加密或密钥交换算法协商出会话密钥,之后用对称加密传输数据;用消息认证码(现代加密套件用 AEAD)保证完整性;用 CA 签发的数字证书证明服务器身份。TLS 1.2 的完整握手需要 2 个 RTT,TLS 1.3 缩短到 1 个 RTT。
详细解析
三个风险和对应的手段
| 风险 | 解决手段 |
|---|---|
| 窃听:链路上的任何节点都能看到明文 | 加密:数据用对称加密传输,对称密钥在握手时通过非对称加密或密钥交换算法协商得到 |
| 篡改:中间节点可以修改内容,比如插入广告 | 完整性校验:消息认证码(MAC);现代加密套件用 AEAD(如 AES-GCM),加密和完整性校验一步完成 |
| 冒充:无法确认对方是不是真正的服务器 | 身份认证:服务器出示 CA 签发的数字证书 |
为什么不全程用非对称加密?因为它计算开销大、比对称加密慢得多,所以只在握手阶段用来安全地协商密钥,之后的数据都用对称加密。
TLS 1.2 握手(以 RSA 密钥交换为例)
客户端 服务端
|--- (1) ClientHello ---------------------------->|
|<---------------------------- (2) ServerHello ---|
|<-------------------------------- Certificate ---|
|<---------------------------- ServerHelloDone ---|
|--- (3) ClientKeyExchange ---------------------->|
|--- ChangeCipherSpec --------------------------->|
|--- Finished ----------------------------------->|
|<----------------------- (4) ChangeCipherSpec ---|
|<----------------------------------- Finished ---|
|<============= encrypted HTTP data =============>|
- ClientHello:客户端发送支持的 TLS 版本、加密套件列表和一个客户端随机数
- ServerHello、Certificate:服务端选定版本和加密套件,发送服务端随机数和自己的证书(含公钥)
- ClientKeyExchange:客户端验证证书,生成预主密钥,用证书里的公钥加密后发送,并用两个随机数和预主密钥算出会话密钥。随后发送 ChangeCipherSpec(表示之后改用加密通信)和 Finished(对之前全部握手消息的校验值,已加密)
- ChangeCipherSpec、Finished:服务端用私钥解密出预主密钥,用同样的方法算出相同的会话密钥,也回复 Finished。双方校验对方的 Finished 无误,握手完成
整个握手需要 2 个 RTT(不含 TCP 握手),之后才开始传输 HTTP 数据。
RSA 的问题和 ECDHE
RSA 密钥交换没有前向安全:预主密钥是用服务器公钥加密后传输的,攻击者只要把流量录下来,日后一旦拿到服务器私钥,就能解密出预主密钥,进而解密全部历史通信。
现在主流使用 ECDHE 密钥交换:双方各自生成临时的密钥对,交换公钥后各自算出相同的预主密钥,预主密钥本身从不在网络上传输。临时私钥用完即丢,服务器证书对应的私钥只用来给 ECDHE 参数签名、证明身份。即使服务器私钥日后泄露,也推算不出过去的会话密钥。在 TLS 1.2 中使用 ECDHE 时,服务端会多发一条 ServerKeyExchange 消息,携带签过名的 ECDHE 公钥;客户端的 ClientKeyExchange 则改为发送自己的 ECDHE 公钥。
TLS 1.3
客户端 服务端
|--- (1) ClientHello + key_share ---------------->|
|<---------------- (2) ServerHello + key_share ---|
|<---------------------- {EncryptedExtensions} ---|
|<----------- {Certificate, CertificateVerify} ---|
|<--------------------------------- {Finished} ---|
|--- (3) {Finished} + HTTP request -------------->|
花括号里的消息已经加密
- 1 个 RTT:客户端在 ClientHello 里直接带上密钥交换参数(key_share),服务端回复 ServerHello 后双方就能算出密钥,证书等后续握手消息都是加密传输的
- 更安全:移除了 RSA、静态 DH 这类没有前向安全的密钥交换方式,对称加密只保留 AEAD 算法
- 0-RTT 会话恢复:再次连接同一服务器时,客户端可以在第一个消息里直接带上应用数据。但 0-RTT 数据可能被截获后重放,只适合幂等的请求(如 GET,见 GET 和 POST 的区别)
HTTP/3 使用的 QUIC 直接集成了 TLS 1.3,把传输层握手和加密握手合在一起(见 HTTP 版本区别)。
证书校验
证书里有域名、公钥、颁发机构(CA)、有效期等信息,以及 CA 的数字签名。
- 数字签名:CA 先对证书内容计算摘要(哈希),再用自己的私钥对摘要签名。浏览器用 CA 的公钥验证签名,确认证书内容没被篡改、确实由这个 CA 签发
- 证书链:服务器证书 → 中间证书 → 根证书。浏览器用上一级 CA 的公钥验证下一级证书的签名,一直验证到操作系统或浏览器内置信任的根证书。服务器在握手时要发送自己的证书和中间证书
- 其他检查:是否在有效期内、域名是否匹配、是否已被吊销
中间人攻击
攻击者夹在客户端和服务器之间,分别和双方通信。它拿不到服务器的私钥,只能出示伪造的证书,浏览器校验不通过就会显示警告;用户如果无视警告继续访问,HTTPS 就失去了保护。
另一种手段是 SSL 剥离(SSL Stripping,也常被叫作 HTTPS 降级攻击):只要第一个请求走的是 HTTP(比如点击了一个 http:// 链接),攻击者就可以拦截它,阻止跳转到 HTTPS,让用户一直用明文和自己通信。HSTS 用来防御这种攻击,见下面的追问。
面试官可能追问
什么是前向安全?
服务器的长期私钥日后泄露,过去的通信也不会被解密。ECDHE 每次握手都生成临时密钥对,会话密钥用完即丢,长期私钥只负责签名,所以具备前向安全;RSA 密钥交换直接用服务器公钥加密预主密钥,私钥一旦泄露,录下来的历史流量就都能被解密。TLS 1.3 已经移除了 RSA 密钥交换。
Charles、Fiddler 为什么能抓到 HTTPS 的明文?
它们充当了中间人:浏览器和抓包工具之间、抓包工具和服务器之间分别建立两段 TLS 连接,抓包工具用自己的根证书为访问的每个域名动态签发证书。浏览器之所以不报错,是因为用户事先在设备上安装并信任了抓包工具的根证书。所以 HTTPS 防的是"不受信任的中间人"。App 如果不想被这样抓包,可以使用证书固定(Certificate Pinning),只信任指定的证书或公钥。
HSTS 是什么?
服务器通过响应头 Strict-Transport-Security: max-age=31536000; includeSubDomains 告诉浏览器:在有效期内,这个域名(及其子域名)只能用 HTTPS 访问。之后即使访问 http:// 地址,浏览器也会在本地直接改成 HTTPS,不发出明文请求,SSL 剥离就失效了;遇到证书错误时,也不允许用户忽略警告继续访问。这个响应头只有通过 HTTPS 返回才生效,HTTP 响应里的会被忽略。第一次访问时还没收到这个响应头,这个空档可以通过把域名加入浏览器内置的 HSTS 预加载列表来弥补。
易错点
- 数据传输用的是对称加密,非对称加密(或密钥交换)只在握手阶段使用
- "握手需要 2 个 RTT"说的是 TLS 1.2 的完整握手,不含 TCP 三次握手;TLS 1.3 只要 1 个 RTT
- 证书报错不一定是攻击,也可能是证书过期、域名不匹配,或者服务器没有配置中间证书
AI 模拟面试官
用自己的话回答,AI 对照参考答案打分、指出遗漏,再追问,最多 3 轮
这道题你掌握了吗?
选一个最接近的状态,没掌握的题会出现在"我的进度 · 待复习"里。
学习记录暂存在本机浏览器。登录后自动同步到账号,换设备也能看到。