3. 密码套件(Cipher Suite)选择与配置归纳总结
密码套件,说白了就是 TLS 握手的“配方”。它决定了双方怎么交换密钥、怎么验证身份、怎么加密数据、怎么保证完整性。我见过不少团队,只管把 OpenSSL 默认套件一放,结果一半流量还在用 RC4 或 3DES——嗯,这其实挺危险的。
这一章我帮你把 TLS 1.2 常见密码套件捋一遍,按安全等级分组,再给出现代推荐配置。看完你就能拿回去直接改生产环境了。
3.1 密码套件的结构
一个密码套件长这样:
TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
└── 协议 密钥交换 认证 加密算法 加密模式 HMAC
每个字段含义如下:
| 字段 | 常见取值 | 安全说明 |
|---|---|---|
| 密钥交换 | ECDHE, DHE, ECDH, RSA | 前向安全性:ECDHE > DHE,RSA 无前向安全 |
| 认证 | RSA, ECDSA, DSA | ECDSA 性能优于 RSA,推荐两者混合部署 |
| 加密算法 | AES-128, AES-256, CHACHA20, 3DES | 3DES 已废弃,CHACHA20 适合移动端 |
| 加密模式 | GCM, CBC, CCM | GCM 提供认证加密,CBC 有 padding oracle 风险 |
| HMAC | SHA384, SHA256, SHA1 | SHA-1 已不推荐,SHA-2 系列是底线 |
核心结论:优先使用 ECDHE + ECDSA(或 RSA)+ AES-256-GCM + SHA384 的组合。退而求其次是 ECDHE + RSA + AES-128-GCM + SHA256。
3.2 TLS 1.2 常用套件安全分级
我个人习惯把套件分成三个梯队——安全、可用、已死。碰到已死的那一批,直接下线。
- ✅ 安全梯队(可保留 TLS 1.2 且推荐优先使用)
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(移动优先)
- ⚠️ 可用梯队(过渡期允许,建议逐步移除)
TLS_DHE_RSA_WITH_AES_256_GCM_SHA384TLS_DHE_RSA_WITH_AES_128_GCM_SHA256TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384(仅当客户端不支持 GCM 时)
- ❌ 已死梯队(必须立即下线)
TLS_RSA_WITH_AES_256_CBC_SHA(无前向安全)TLS_RSA_WITH_3DES_EDE_CBC_SHA(3DES 已破)TLS_ECDHE_RSA_WITH_RC4_128_SHA(RC4 被攻击)TLS_RSA_WITH_RC4_128_MD5(MD5 + RC4 双废)
特别注意:即便你只开启 ECDHE + AES-GCM,如果服务器支持 RSA 密钥交换(套件名不含 DHE/ECDHE),中间人攻击者仍可能降级。建议在 Nginx 或 Apache 配置中明确只允许 ECDHE 和 DHE 类套件。
3.3 配置实战:Nginx + OpenSSL
下面是一个生产环境用的 Nginx 密码套件配置。我自己的服务器用了快两年,没出过兼容性问题。
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256';
ssl_prefer_server_ciphers on;
解释几点:
ssl_prefer_server_ciphers on:强制服务端优先选择,防止客户端用弱套件。- 把 CHACHA20 放在 GCM 后面——因为桌面端硬件加速 AES-GCM 更快,移动端才用 CHACHA20。
- 没有加任何 CBC 模式套件。原因?我在 2019 年处理过一个 case:客户使用老旧 Java 客户端,只能连 CBC。我们不得不单独为那台服务器保留 CBC,其他服务器全禁用。后来统一升级了客户端才解决。
3.4 旧客户端兼容性处理
有些场景——比如银行柜面终端、工控机——还在用 Windows XP 或 Java 6。这些客户端不支持 ECDHE,甚至不支持 TLS 1.2。怎么办?
- 方案 A:双端口。一个端口(443)只开 ECDHE + GCM 强套件,另一个端口(类似 8443)额外开 DHE + CBC 弱套件。用 IP 白名单控制弱端口访问。
- 方案 B:应用层协议协商。客户端先通过 HTTP 请求头声明能力,服务端动态选择套件。但实现复杂,我一般不用。
- 方案 C:升级客户端。最彻底的方法。我曾帮一家物流企业写过一个工具,检测所有内部系统依赖的 TLS 套件,强制要求升级到至少 OpenSSL 1.0.2。半年后全线切换完成。
小技巧:用 `openssl ciphers -v 'ECDHE+AES-GCM:!aNULL:!eNULL:!LOW'` 命令可以看实际生效列表。我每次改配置都会跑一遍这个命令,核对没有“已死”套件漏进去。
3.5 密码套件知识体系图
下面我把密码套件的选择逻辑总结成一张图。你跟着这个流程走,基本不会选错。
3.6 验证配置是否正确
配置完了别着急上线。先跑这几个检查。我每次都会做一遍:
- 命令行测试:
openssl s_client -connect yoursite.com:443 -cipher ECDHE 2>/dev/null | grep "Cipher is",看返回的套件是否符合预期。 - 弱套件准入测试:用
openssl s_client -cipher 'RC4'尝试连接,应该返回 handshake failure。 - 第三方扫描:用 sslscan 或 testssl.sh 扫一遍。我常用 testssl.sh 的
--cipher-per-proto参数检查每个协议版本下的套件列表。 - 兼容性压测:在预发环境模拟真实用户流量,观察有多少连接因套件不匹配而失败。正常情况下失败率应低于 0.1%。
我曾经踩过的坑:有一次我配置了 ECDHE 优先,但忘了把非 ECDHE 套件全部删除。结果老客户端仍然协商到了 RSA 密钥交换。后来我强制在配置里加了
ssl_ciphers '...:!kRSA'; 才彻底堵住。
3.7 总结一张对照表
最后给一张快速对照表,方便你日常查阅:
| 应用场景 | 推荐主套件 | 备选套件 | 备注 |
|---|---|---|---|
| 通用 Web 服务器(桌面+移动) | ECDHE-RSA-AES256-GCM-SHA384 | ECDHE-ECDSA-AES128-GCM-SHA256 | OpenSSL 1.1.1+ 支持 |
| 移动 App 后端 | ECDHE-ECDSA-CHACHA20-POLY1305 | ECDHE-RSA-CHACHA20-POLY1305 | CHACHA20 省电 |
| 嵌入式/低功耗设备 | ECDHE-RSA-AES128-GCM-SHA256 | DHE-RSA-AES128-GCM-SHA256 | 减少计算量 |
| 兼容老客户端(Java 6 / XP) | DHE-RSA-AES128-GCM-SHA256 | DHE-RSA-AES256-CBC-SHA384 | 仅限隔离端口 |
| 完全下线 TLS 1.2 | (无) | 仅保留 TLS 1.3 套件 | 需确认客户端都支持 TLS 1.3 |