关于启用 HTTPS 的一些经验分享(二)

2015/12/24 · 基础技术 ·
HTTP,
HTTPS

原文出处:
imququ(@屈光宇)   

文章目录

  • SSL 版本选择
  • 加密套件选择
  • SNI 扩展
  • 证书选择

几天前,一位朋友问我:都说推荐用 Qualys SSL
Labs 这个工具测试 SSL
安全性,为什么有些安全实力很强的大厂家评分也很低?我认为这个问题应该从两方面来看:1)国内用户终端情况复杂,很多时候降低
SSL 安全配置是为了兼容更多用户;2)确实有一些大厂家的 SSL
配置很不专业,尤其是配置了一些明显不该使用的 CipherSuite。

我之前写的《关于启用 HTTPS
的一些经验分享(一)》,主要介绍 HTTPS
如何与一些新出的安全规范配合使用,面向的是现代浏览器。而今天这篇文章,更多的是介绍启用
HTTPS 过程中在老旧浏览器下可能遇到的问题,以及如何取舍。

几天前,一位朋友问我:都说推荐用 Qualys SSL
Labs 这个工具测试 SSL
安全性,为什么有些安全实力很强的大厂家评分也很低?我认为这个问题应该从两方面来看:

如何针对老旧浏览器设置 HTTPS 策略

几天前,一位朋友问我:都说推荐用 Qualys SSL Labs 这个工具测试 SSL
安全性,为什么有些安全实力很强的大厂家评分也很低?我认为这个问题应该从两方面来看:

  1. 国内用户终端情况复杂,很多时候降低 SSL 安全配置是为了兼容更多用户;
  2. 确实有一些大厂家的 SSL 配置很不专业,尤其是配置了一些明显不该使用的
    CipherSuite。

我之前写的《关于启用 HTTPS 的一些经验分享(一)》,主要介绍 HTTPS
如何与一些新出的安全规范配合使用,面向的是现代浏览器。而今天这篇文章,更多的是介绍启用
HTTPS 过程中在老旧浏览器下可能遇到的问题,以及如何取舍。

图片 1

 

在最近几年里,我写了很多有关 HTTPS 和 HTTP/2
的文章,涵盖了证书申请、Nginx
编译及配置、性能优化等方方面面。在这些文章的评论中,不少读者提出了各种各样的问题,我的邮箱也经常收到类似的邮件。本文用来罗列其中有代表性、且我知道解决方案的问题。

SSL 版本选择

TLS(Transport Layer Security,传输层安全)的前身是 SSL(Secure Sockets
Layer,安全套接字层),它最初的几个版本(SSL 1.0、SSL 2.0、SSL
3.0)由网景公司开发,从 3.1 开始被 IETF 标准化并改名,发展至今已经有 TLS
1.0、TLS 1.1、TLS 1.2 三个版本。TLS 1.3 改动会比较大,目前还在草案阶段。

SSL 1.0 从未公开过,而 SSL 2.0 和 SSL 3.0
都存在安全问题,不推荐使用。Nginx 从 1.9.1 开始默认只支持 TLS
的三个版本,以下是 Nginx
官方文档中对
ssl_protocols 配置的说明:

Syntax: ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1]
[TLSv1.2];
Default: ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
Context: http, server
Enables the specified protocols. The TLSv1.1 and TLSv1.2 parameters
work only when the OpenSSL library of version 1.0.1 or higher is used.

但不幸的是,IE 6 只支持 SSLv2 和
SSLv3(来源),也就是说
HTTPS 网站要支持 IE 6,就必须启用 SSLv3。仅这一项就会导致 SSL Labs
给出的评分降为 C。

  1. 国内用户终端情况复杂,很多时候降低 SSL 安全配置是为了兼容更多用户;
  2. 确实有一些大厂家的 SSL 配置很不专业,尤其是配置了一些明显不该使用的
    CipherSuite。

SSL 版本选择

TLS(传输层安全(Transport Layer Security))的前身是
SSL(安全套接字层(Secure Sockets Layer)),它最初的几个版本(SSL
1.0、SSL 2.0、SSL 3.0)由网景公司开发,从 3.1 开始被 IETF
标准化并改名,发展至今已经有 TLS 1.0、TLS 1.1、TLS 1.2 三个版本。TLS 1.3
改动会比较大,目前还在草案阶段。

SSL 1.0 从未公开过,而 SSL 2.0 和 SSL 3.0
都存在安全问题,不推荐使用。Nginx 从 1.9.1 开始默认只支持 TLS
的三个版本,以下是 Nginx 官方文档中对 ssl_protocols 配置的说明:

Syntax: ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2];
Default: ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
Context: http, server
Enables the specified protocols. The TLSv1.1 and TLSv1.2 parameters work only when the OpenSSL library of version 1.0.1 or higher is used.

但不幸的是,IE 6 只支持 SSLv2 和 SSLv3(来源),也就是说 HTTPS
网站要支持 IE 6,就必须启用 SSLv3。仅这一项就会导致 SSL Labs
给出的评分降为 C。

 

为了控制篇幅,本文尽量只给出结论和引用链接,不展开讨论,如有疑问或不同意见,欢迎留言讨论。本文会持续更新,欢迎大家贡献自己遇到的问题和解决方案。

加密套件选择

加密套件(CipherSuite),是在 SSL
握手中需要协商的很重要的一个参数。客户端会在 Client Hello
中带上它所支持的 CipherSuite 列表,服务端会从中选定一个并通过
Server Hello 返回。如果客户端支持的 CipherSuite 列表与服务端配置的
CipherSuite 列表没有交集,会导致无法完成协商,握手失败。

CipherSuite
包含多种技术,例如认证算法(Authentication)、加密算法(Encryption)、消息认证码算法(Message
Authentication Code,简称为 MAC)、密钥交换算法(Key
Exchange)和密钥衍生算法(Key Derivation Function)。

SSL 的 CipherSuite 协商机制具有良好的扩展性,每个 CipherSuite 都需要在
IANA 注册,并被分配两个字节的标志。全部 CipherSuite 可以在 IANA 的 TLS
Cipher Suite
Registry
页面查看。

OpenSSL 库支持的全部 CipherSuite 可以通过以下命令查看:

openssl ciphers -V | column -t 0xCC,0x14 – ECDHE-ECDSA-CHACHA20-POLY1305
TLSv1.2 Kx=ECDH Au=ECDSA Enc=ChaCha20-Poly1305 Mac=AEAD … …

1
2
3
openssl ciphers -V | column -t
0xCC,0x14  –  ECDHE-ECDSA-CHACHA20-POLY1305  TLSv1.2  Kx=ECDH        Au=ECDSA   Enc=ChaCha20-Poly1305  Mac=AEAD
… …

0xCC,0x14 是 CipherSuite 的编号,在 SSL
握手中会用到。ECDHE-ECDSA-CHACHA20-POLY1305
是它的名称,之后几部分分别表示:用于 TLSv1.2,使用 ECDH 做密钥交换,使用
ECDSA 做认证,使用 ChaCha20-Poly1305 做对称加密,由于 ChaCha20-Poly1305
是一种 AEAD 模式,不需要 MAC 算法,所以 MAC 列显示为 AEAD。

要了解 CipherSuite 的更多内容,可以阅读这篇长文《TLS 协议分析 与
现代加密通信协议设计》。总之,在配置
CipherSuite 时,请务必参考权威文档,如:Mozilla
的推荐配置、CloudFlare
使用的配置。

以上 Mozilla 文档中的「Old backward compatibility」配置,以及 CloudFlare
的配置,都可以很好的兼容老旧浏览器,包括 Windows XP / IE6。

之前见到某个大厂家居然支持包含 EXPORT
CipherSuite,这些套件在上世纪由于美国出口限制而被弱化过,已被攻破,实在没有理由再使用。

我之前写的《关于启用 HTTPS
的一些经验分享(一)》,主要介绍
HTTPS
如何与一些新出的安全规范配合使用,面向的是现代浏览器。而今天这篇文章,更多的是介绍启用
HTTPS 过程中在老旧浏览器下可能遇到的问题,以及如何取舍。

加密套件选择

加密套件(CipherSuite),是在 SSL
握手中需要协商的很重要的一个参数。客户端会在 Client Hello 中带上它所支持的
CipherSuite
列表,服务端会从中选定一个并通过 Server Hello 返回。如果客户端支持的
CipherSuite 列表与服务端配置的 CipherSuite
列表没有交集,会导致无法完成协商,握手失败。

CipherSuite
包含多种技术,例如认证算法(Authentication)、加密算法(Encryption)、消息认证码算法(Message
Authentication Code)(MAC)、密钥交换算法(Key
Exchange)和密钥衍生算法(Key Derivation Function)。

SSL 的 CipherSuite 协商机制具有良好的扩展性,每个 CipherSuite 都需要在
IANA 注册,并被分配两个字节的标志。全部 CipherSuite 可以在 IANA 的 TLS
Cipher Suite Registry 页面查看。

OpenSSL 库支持的全部 CipherSuite 可以通过以下命令查看:

  1. openssl ciphers -V | column -t
  2. 0xCC,0x14- ECDHE-ECDSA-CHACHA20-POLY1305 TLSv1.2Kx=ECDH Au=ECDSA Enc=ChaCha20-Poly1305Mac=AEAD
  3. ......

0xCC,0x14 是 CipherSuite 的编号,在 SSL
握手中会用到。ECDHE-ECDSA-CHACHA20-POLY1305 是它的名称,之后几部分分别表示:用于
TLSv1.2,使用 ECDH 做密钥交换,使用 ECDSA 做认证,使用 ChaCha20-Poly1305
做对称加密,由于 ChaCha20-Poly1305 是一种 AEAD 模式,不需要 MAC
算法,所以 MAC 列显示为 AEAD。

要了解 CipherSuite 的更多内容,可以阅读这篇长文《TLS 协议分析 与
现代加密通信协议设计》。总之,在配置 CipherSuite
时,请务必参考权威文档,如:Mozilla 的推荐配置、CloudFlare 使用的配置。

以上 Mozilla 文档中的「Old backward compatibility」配置,以及 CloudFlare
的配置,都可以很好的兼容老旧浏览器,包括 Windows XP / IE6。

之前见到某个大厂家居然支持包含 EXPORT 的
CipherSuite,这些套件在上世纪由于美国出口限制而被弱化过,已被攻破,实在没有理由再使用。

 

实际上,遇到任何有关部署 HTTPS 或 HTTP/2 的问题,都推荐先用 Qualys SSL
Labs’s SSL Server
Test 跑个测试,大部分问题都能被诊断出来。

SNI 扩展

我们知道,在 Nginx 中可以通过指定不同的 server_name
来配置多个站点。HTTP/1.1 协议请求头中的 Host
字段可以标识出当前请求属于哪个站点。但是对于 HTTPS 网站来说,要想发送
HTTP 数据,必须等待 SSL
握手完成,而在握手阶段服务端就必须提供网站证书。对于在同一个 IP 部署不同
HTTPS 站点,并且还使用了不同证书的情况下,服务端怎么知道该发送哪个证书?

Server Name Indication,简称为 SNI,是 TLS
的一个扩展,为解决这个问题应运而生。有了 SNI,服务端可以通过
Client Hello 中的 SNI 扩展拿到用户要访问网站的 Server
Name,进而发送与之匹配的证书,顺利完成 SSL 握手。

Nginx 在很早之前就支持了 SNI,可以通过 nginx -V
来验证。以下是我的验证结果:

./nginx -V nginx version: nginx/1.9.9 built by gcc 4.8.4 (Ubuntu
4.8.4-2ubuntu1~14.04) built with OpenSSL 1.0.2e-dev xx XXX xxxx TLS SNI
support enabled configure arguments: –with-openssl=../openssl
–with-http_ssl_module –with-http_v2_module

1
2
3
4
5
6
./nginx -V
nginx version: nginx/1.9.9
built by gcc 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04)
built with OpenSSL 1.0.2e-dev xx XXX xxxx
TLS SNI support enabled
configure arguments: –with-openssl=../openssl –with-http_ssl_module –with-http_v2_module

然而,并不是所有浏览器都支持 SNI,以下是常见浏览器支持 SNI 的最低版本:

浏览器 最低版本
Chrome Vista+ 全支持;XP 需要 Chrome 6+;OSX 10.5.7+ 且 Chrome 5+
Firefox 2.0+
Internet Explorer 7+ (需要 Vista+)
Safari 3+ (需要 OS X 10.5.6+)
Mobile Safari iOS 4.0+
Android Webview 3.0+

如果要避免在不支持 SNI 的浏览器中出现证书错误,只能将使用不同证书的
HTTPS 站点部署在不同 IP 上,最简单的做法是分开部署到不同机器上。

图片 2

SNI 扩展

我们知道,在 Nginx
中可以通过指定不同的 server_name 来配置多个站点。HTTP/1.1
协议请求头中的 Host 字段可以标识出当前请求属于哪个站点。但是对于 HTTPS
网站来说,要想发送 HTTP 数据,必须等待 SSL
握手完成,而在握手阶段服务端就必须提供网站证书。对于在同一个 IP 部署不同
HTTPS 站点,并且还使用了不同证书的情况下,服务端怎么知道该发送哪个证书?

Server Name Indication,简称为 SNI,是 TLS
的一个扩展,为解决这个问题应运而生。有了
SNI,服务端可以通过 Client Hello 中的 SNI 扩展拿到用户要访问网站的
Server Name,进而发送与之匹配的证书,顺利完成 SSL 握手。

Nginx 在很早之前就支持了
SNI,可以通过 nginx -V 来验证。以下是我的验证结果:

  1. ./nginx -V
  2. nginx version: nginx/1.9.9
  3. built by gcc4.8.4(Ubuntu4.8.4-2ubuntu1~14.04)
  4. built withOpenSSL1.0.2e-dev xx XXX xxxx
  5. TLS SNI support enabled
  6. configure arguments:--with-openssl=../openssl --with-http_ssl_module --with-http_v2_module

然而,并不是所有浏览器都支持 SNI,以下是常见浏览器支持 SNI 的最低版本:

浏览器 最低版本
Chrome Vista+ 全支持;XP 需要 Chrome 6+;OSX 10.5.7+ 且 Chrome 5+
Firefox 2.0+
Internet Explorer 7+ (需要 Vista+)
Safari 3+ (需要 OS X 10.5.6+)
Mobile Safari iOS 4.0+
Android Webview 3.0+

可以看到,现在还有一定用户量的 Windows XP IE6~8、Android 2.x Webview
都不支持 SNI。如果要避免在这些浏览器中出现证书错误,只能将使用不同证书的
HTTPS 站点部署在不同 IP 上,最简单的做法是分开部署到不同机器上。

 

申请 Let’s Encrypt 证书时,一直无法验证通过

这类问题一般是因为 Let’s Encrypt
无法访问你的服务器,推荐尝试 acme.sh 的 DNS
验证模式,一般都能解决。

Author

发表评论

电子邮件地址不会被公开。 必填项已用*标注