HTTPS 真的能防止重放攻击吗?
面试中常遇到这类问题:面试官问起 HTTPS 流程,候选人通常能复述握手步骤,但问到细节时容易卡壳。比如中间人重放请求,HTTPS 能否防御?
带着这个疑问,我们深入 TLS 协议内部看看。
协议流程回顾
HTTPS = HTTP + SSL/TLS。以 RSA 密钥交换为例,标准流程如下:
- 客户端生成随机数
client_random、TLS 版本号,发送给服务端。 - 服务端返回自己的随机数
server_random、证书及公钥。 - 客户端验证证书有效性,提取服务器公钥。
- 客户端生成预主密钥
pre_master_secret,用公钥加密后发送。 - 服务端用私钥解密获取
pre_master_secret。 - 双方利用
client_random、server_random和pre_master_secret计算出对称密钥(Master Secret),后续通信使用对称加密。
很多文章只讲到生成了对称密钥就结束,但这步之后的密钥派生过程才是关键。
密钥是如何生成的?
拿到三个参数后,双方会推导出 master_secret。一旦推导完成,pre_master_secret 必须立即删除(RFC 2246 Section 8.1)。
接着计算 key_block(会话密钥块),公式如下:
text
key_block = PRF(master_secret, "key expansion", server_random + client_random)
这里的 PRF 是伪随机函数,具体算法取决于 TLS 版本(如 SHA256)。基于 key_block,双方可进一步推导出 6 个密钥值:
client_write_MAC_key/server_write_MAC_key(消息认证码密钥)client_write_key/server_write_key(对称加密密钥)client_write_IV/server_write_IV(初始化向量)
这六把密钥由两端共同持有,但方向不同。例如客户端用 client_write_key 加密数据,服务端则用同样的密钥解密;反之亦然。
MAC 与序列号机制
有了密钥,数据如何加密?TLS 支持流加密、分组加密和 AEAD 模式。我们以流加密模式为例说明防重放的核心逻辑。
在加密前,需先计算 MAC 验证码以保证完整性。其公式为(RFC 2246 Section 6.2.3.1):
text
HMAC_hash(MAC_write_secret, seq_num + TLSCompressed.type + \
TLSCompressed.version + TLSCompressed.length + \
TLSCompressed.fragment)
注意入参中的 seq_num(序列号)。这就是防止重放攻击的关键。
序列号如何工作?
连接建立后,客户端和服务端各自维护发送和接收的计数器:
- 初始化:
client_send、client_recv、、 默认均为 0。


