Nginx禁用TLS1.0与1.1的尝试
作为一个开发者,自己的博客应该是一个追求现代化的网站
(主要还是因为没什么 PV),所以我作出了一个愉快的的决定:禁用 TLS1.0 和 TLS1.1。
SSL/TLS 相关
什么是 SSL/TLS
传输层安全性协议(英语:Transport Layer Security,缩写作 TLS),及其前身安全套接层(Secure Sockets Layer,缩写作 SSL)是一种安全协议,目的是为互联网通信提供安全及数据完整性保障。网景公司(Netscape)在 1994 年推出首版网页浏览器,网景导航者时,推出 HTTPS 协议,以 SSL 进行加密,这是 SSL 的起源。IETF 将 SSL 进行标准化,1999 年公布第一版 TLS 标准文件。随后又公布 RFC 5246 (2008 年 8 月)与 RFC 6176 (2011 年 3 月)。在浏览器、电子邮件、即时通信、VoIP、网络传真等应用程序中,广泛支持这个协议。主要的网站,如 Google、Facebook 等也以这个协议来创建安全连线,发送数据。当前已成为互联网上保密通信的工业标准。
SSL/TLS 的版本
协议 | 发布时间 | 状态 |
---|---|---|
SSL 1.0 | 未公布 | 未公布 |
SSL 2.0 | 1995 年 | 已于 2011 年弃用 |
SSL 3.0 | 1996 年 | 已于 2015 年弃用 |
TLS 1.0 | 1999 年 | 计划于 2020 年弃用 |
TLS 1.1 | 2006 年 | 计划于 2020 年弃用 |
TLS 1.2 | 2008 年 | |
TLS 1.3 | 2018 年 |
为什么要禁用 TLS1.0、TLS1.1
SSL 由于以往发现的漏洞,已经被证实不安全。而 TLS1.0 与 SSL3.0 的区别实际上并不太多,并且 TLS1.0 可以通过某些方式被强制降级为 SSL3.0。
由此,支付卡行业安全标准委员会(PCI SSC)强制取消了支付卡行业对 TLS 1.0 的支持,同时强烈建议取消对 TLS 1.1 的支持。
苹果、谷歌、微软、Mozilla 也发表了声明,将于 2020 年初放弃对 TLS 1.1 和 TLS 1.0 的支持。其原因是这两个版本使用的是过时的算法和加密系统,经发现,这些算法和系统是十分脆弱的,比如 SHA-1 和 MD5。它们也缺乏像完美的前向保密性这样的现代特征,并且容易受到降级攻击的影响。
在 Nginx 上禁用 TLS1.0、TLS1.1
排查自己的网站
实际我想要禁用 TLS1.0 和 TLS1.1 的一大原因是,在SSL Labs和My SSL做的 SSL 检测时,发现我的某个博客启用了 TLS1.0 及配套的加密套件,导致评分直接掉到了 B 级。
由于本博客是托管在 Github Page 上的,启用的即 Github Page 的 TLS 配置,故不存在此问题。
尝试在 Nginx 禁用 TLS1.0 和 TLS1.1
首先登陆自己的主机,查看 Nginx 版本
1 | nginx -V |
这个版本的 Nginx 默认支持的 TLS 版本,即为上述检测出来的 TLS1.0、TLS1.1、TLS1.2。
那么,如果要禁用 TLS1.0 及 TLS1.1 的话,只需要在 Nginx 配置文件中,在指定的域名上指定支持的 TLS 的版本即可。
我所指定的如下
1 | ssl_protocols TLSv1.2 TLSv1.3; |
该配置方式仅作为演示用途,对于推荐的配置方式,可以在Cipherli.st获取。
保存 Nginx 配置后,重启 Nginx,通过 openssl 指令查询对应的 TLS1.0 版本是否仍旧有效:
1 | openssl s_client -connect mydomain.com:443 -tls1 |
然而发现仍旧返回了验证通过,这是怎么回事呢?
SNI 协议与 Nginx 的 default server
仔细看了上边命令的返回,我发现实际返回的域名和我测试所请求的域名并不一致。这是怎么回事呢?
原因是我在这个服务器上部署的域名和网站不止一个。在 HTTP 协议中,请求的域名作为主机头(Host)放在 HTTP Header 中,而在 SSL 握手的时候,SSL 拿不到 Host 的信息,这时服务端通常返回的是配置中第一个可用的证书。
这个问题是早期 SSL2.0 时就引入的一个问题,由此 SNI 协议就诞生了。SNI 协议允许客户端在发起 SSL 握手请求时,就提交请求的 Host 信息,使服务器能够切换到正确的域并返回相应的证书。
SNI(Server Name Indication)是为了解决一个服务器使用多个域名和证书的 SSL/TLS 扩展。一句话简述它的工作原理就是,在连接到服务器建立 SSL 连接之前先发送要访问站点的域名(Hostname),这样服务器根据这个域名返回一个合适的证书。目前,大多数操作系统和浏览器都已经很好地支持 SNI 扩展,OpenSSL 0.9.8 已经内置这一功能。
从上述 Nginx 命令中可以看出,我的 Nginx 是支持 SNI 协议的,而在上面没有返回正确证书的原因是,我的 openssl 命令并没有带上 SNI 协议查询。如果要带上 SNI 协议来查询的话,命令应该增加-servername 参数
1 | openssl s_client -servername mydomain.com -connect mydomain.com:443 -tls1 |
可以看出,命令返回了错误信息,即 TLS1.0 确实被禁用了。
我们再回头看之前的错误中,还有一个问题。在客户端不支持 SNI 的情况下,服务端默认返回的是配置中第一个可用的证书。那么如果我们希望服务端返回特定的证书的话应该怎么办?
Nginx 有一个 default server 的概念。也就是说当出现不支持 SNI 协议的客户端时,将使用 default server 的配置进行验证。当没有配置 default server 的时候,Nginx 将使用找到的第一个配置文件中的配置(经过测试,应该是按照字母顺序排序的)。
我们需要在 Nginx 配置文件中,将指定的域名中加入 default_server,这样的话,在客户端不支持 SNI 时,Nginx 就会将该域名作为默认的配置。
1 | server { |
为了以防万一,我把我所有的域名均禁用了 TLS1.0 和 TLS1.1。
再次通过 My SSL 检测后,评级上升到了 A。
原本我还计划添加对 TLS1.3 的支持,但由于目前主流浏览器和客户端对 TLS1.3 的支持并不够完善,并且似乎我需要更新一些服务器配置,需要继续探究一下相关的知识,所以就没有在本次文章中写出。也许会在下一篇文章写吧!(Flag 已经立起来了)