SSL连接恢复
SSL/TLS 连接断开后的两种恢复机制
问题
SSL 连接断开后如何恢复?为什么需要连接恢复机制?
解答
SSL/TLS 完整握手需要 2-RTT(往返时间),计算开销大。连接恢复机制可以跳过部分握手步骤,提升性能。
1. Session ID 恢复
服务器为每个会话分配唯一 ID,客户端重连时携带该 ID,服务器查找缓存恢复会话。
# 完整握手流程
Client Server
|---- ClientHello ---------------->|
|<--- ServerHello + Certificate ---|
|<--- ServerKeyExchange -----------|
|<--- ServerHelloDone -------------|
|---- ClientKeyExchange ---------->|
|---- ChangeCipherSpec ----------->|
|---- Finished ------------------->|
|<--- ChangeCipherSpec ------------|
|<--- Finished --------------------|
# Session ID 恢复流程(1-RTT)
Client Server
|---- ClientHello + SessionID ---->|
|<--- ServerHello + SessionID -----|
|<--- ChangeCipherSpec ------------|
|<--- Finished --------------------|
|---- ChangeCipherSpec ----------->|
|---- Finished ------------------->|
Nginx 配置示例:
server {
listen 443 ssl;
# 启用 Session 缓存
ssl_session_cache shared:SSL:10m; # 10MB 共享缓存
ssl_session_timeout 10m; # 会话有效期 10 分钟
}
2. Session Ticket 恢复
服务器将会话状态加密后发给客户端保存,重连时客户端携带 Ticket,服务器解密恢复会话。
server {
listen 443 ssl;
# 启用 Session Ticket
ssl_session_tickets on;
ssl_session_ticket_key /path/to/ticket.key; # 加密密钥
}
3. TLS 1.3 的 0-RTT 恢复
TLS 1.3 支持 0-RTT 恢复,首次请求就可以携带加密数据:
Client Server
|---- ClientHello + EarlyData --->| # 首包就带数据
|<--- ServerHello + Finished -----|
|---- Finished ------------------>|
Node.js 启用 0-RTT:
const tls = require('tls');
const fs = require('fs');
const server = tls.createServer({
key: fs.readFileSync('server-key.pem'),
cert: fs.readFileSync('server-cert.pem'),
// TLS 1.3 默认启用,支持 0-RTT
minVersion: 'TLSv1.3'
});
server.on('secureConnection', (socket) => {
// 检查是否为恢复的会话
if (socket.isSessionReused()) {
console.log('Session resumed');
}
});
server.listen(443);
两种方式对比
| 特性 | Session ID | Session Ticket |
|---|---|---|
| 状态存储 | 服务器端 | 客户端 |
| 服务器内存 | 需要缓存 | 无需缓存 |
| 分布式支持 | 需要共享缓存 | 只需共享密钥 |
| 安全性 | 较高 | 依赖密钥管理 |
关键点
- Session ID 由服务器存储会话状态,需要内存缓存
- Session Ticket 由客户端存储加密的会话状态,服务器无状态
- 两种方式都能将握手从 2-RTT 降到 1-RTT
- TLS 1.3 支持 0-RTT,但存在重放攻击风险,仅适用于幂等请求
- 分布式环境下 Session Ticket 更易扩展,只需同步加密密钥
目录