HTTPS需要升级TLS加密算法
2013-03-28 16:19:19

关于 HTTPS 协议

本节详细介绍了 HTTPS 相较于 HTTP 更安全的原因,包括对称加密、非对称加密、完整性摘要、数字证书以及 SSL/TLS 握手等内容,图文并茂、理论与实战结合。

1. 不安全的 HTTP

近些年来,越来越多的网站使用 HTTPS 协议进行数据传输,原因在于 HTTPS 相较于 HTTP 能够提供更加安全的服务。很多浏览器对于使用 HTTP 协议的网站会加上『警告』的标志表示数据传输不安全,而对于使用 HTTPS 协议的网站会加上一把『锁』标志表示数据传输安全。为什么 HTTP 协议不安全呢?主要表现在以下三个方面:

容易被窃听:HTTP 传输的数据是明文。黑客很容易通过嗅探技术截获报文,由于数据没有加密,内容可以被黑客所理解。

举个例子:如果用户输入密码取款,那么黑客窃听了此密码后,就可以为所欲为了!

容易被篡改:黑客可以在截获 HTTP 报文后,对报文进行修改,然后再发送到目的地。

举个例子:如果用户想要转账给家人,而黑客将收款人修改成了自己,将会造成用户出现损失!

容易被伪造身份:黑客可以伪造 HTTP 报文,假装自己是用户真正想要访问的网站,然后与用户进行通信。

举个例子:如果用户想要访问淘宝网站进行购物,而黑客冒充自己是淘宝网站,用户就可能在此假淘宝网站上买东西,造成损失!

HTTPS 是如何解决以上安全性问题的呢?主要方法如下所示:

数据加密:HTTPS 传输的不再是明文,而是采用加密算法传输密文,黑客即使截获了报文,也无法理解内容!

完整性摘要:HTTPS 通过摘要算法得到报文的一个摘要,如果黑客篡改了报文内容,那么重新生成的摘要将发生变化,接收方校验后就知道数据不再完整,被篡改了!

数字证书:HTTPS 通过数字证书来验证通信实体的身份,而黑客因为没有相应的证书,一旦冒充其他网站将会被识破!

2. 加密算法

加密算法用于解决 HTTP 传输数据容易被窃听的问题。为了防止传输数据被黑客所窃听,客户端与服务器之间需要对数据进行加解密处理。发送方使用加密算法将明文加密为密文,而接收方使用相应的解密算法将密文解密为明文。黑客只能看到密文,因而并不能获取到任何有用信息。


一般来说,加密算法分为两大类,对称加密和非对称加密。

对称加密:指加密和解码使用同一把密钥,即图中的密钥 A 等于密钥 B;

非对称加密:指加密和解密使用不同的密钥,即图中的密钥 A 不等于密钥 B。

(1)对称加密

对称加密算法中加密和解密的钥匙是同一把,称之为密钥。凯撒密码是一种较为简单的对称加密算法,可用于对英语文本进行加解密。其主要思想是:将明文中的每个字母按照字母表所在位置右移 K 位,得到密文(允许回绕)。

举个例子,设 K = 2,那么明文中的字母 "a" 用字母 "c" 代替,字母 "z" 用 字母 "b" 代替。此时 K = 2 就是对称加密算法中的密钥。


这种方式的缺点在于:每个字母经过加密后只有唯一的密文表示,如果黑客收集了很多数据后进行统计分析,很可能就破解了加密手段。更好的方式是采用多个凯撒密码 K 轮询进行加密,比如位置为奇数的字母采用密钥 K = 2 加密,位置为偶数的字母采用密钥 K = 3 加密。


然而凯撒密码只能加密英文文本,若想要加密所有字符,可以采用分组加密的方式。任何数据在计算机中实际存储的是 0/1 比特的组合。分组加密的主要思想是:将要加密的报文处理为 K 比特的分组,每个分组通过一对一的映射表进行加密。

举个例子,设 K = 3,映射表如下图,那么报文 010110001111 将会被加密为 101000111001。此时 K=3 以及映射表就是对称加密算法中的密钥。


与前面采用多个凯撒密码 K 作为密钥的方式一样,为了增加破解的难度,一种更好的方式是采用多个映射表,轮询对数据进行加密。计算机网络中常用的对称加密算法有:DES、3DES、AES 等,都属于分组加密算法。

(2)非对称加密

非对称加密算法中加密和解密的钥匙不同,分别称为公钥和私钥。其特点在于:

如果用公钥加密,则只能用私钥解密,此时公钥是不能解密的。

如果用私钥加密,则只能用公钥解密,此时私钥是不能解密的。

公钥是对外公开的,任何人都能够得到;私钥只有自己知道,不能泄露。

为什么有了对称加密后还会出现非对称加密呢?

原因在于对称加密的前提是通信双方需要商量出一个密钥,而商量密钥的时候传输的是明文,如果此密钥被黑客所截获,即使后面的报文进行了加密,黑客也可以通过此密钥进行解密!


非对称加密的一个特点是:公钥加密,只有私钥可以解密。那么就无需像对称加密那样提前协商好密钥。通信双方可以直接将自己的公钥发送给另一方,这个公钥即使黑客知道也无所谓,当一方用此公钥加密后,即使黑客截获了报文,也无法用公钥解密,只有拥有私钥的另一方才能解密成功!


计算机网络中常用的非对称加密算法有:RSA、 ECDHE 等。相较于对称加密,非对称加密算法更加复杂难懂,数学推理较多。

(3)混合加密

前面提到,对称加密算法需要提前协商出密钥,而协商的过程用的是明文(因为还没有密钥),如果黑客截获了明文密钥,那么之后即使加密了,黑客也可以用密钥进行解密,此时就无安全性可言了!

非对称加密算法解决了此问题,但是其存在大量的指数运算,加密速度非常慢!而对称加密算法的加密速度非常快,一般是非对称加密算法的 100-10000 倍!

那能不能将二者综合起来,使得数据传输不仅安全且高效呢?答案是肯定的!HTTPS 采用混合加密方式,既采用对称加密,也采用非对称加密。

对称加密算法的弱点在于协商密钥的过程采用明文不安全,存在密钥泄漏的可能,那么我们是不是可以不采用明文,而是采用非对称加密算法来协商此密钥,之后传输数据时再采用对称加密算法进行加密。

也就是说,用非对称加密算法传输密钥,用对称加密算法传输实际数据。此密钥一般称为『会话密钥』。

会话密钥通过非对称加密算法传输,非常安全;

大量数据通过对称加密算法传输(多次),会话密钥只需要传一次,非常高效!    



3. 摘要算法

摘要算法用于解决 HTTP 传输数据容易被篡改的问题。也称为哈希算法,其输入为任意数据,输出为固定长度的字符串(称为摘要)。主要特点如下:

不可逆,即无法通过输出反推输入。

相同的输入必会产生相同的输出。

不同的输入大概率会产生不同的输出。

无论输入的数据有多长,输出摘要的长度固定不变。

举个例子:如果将数据的比特流每 8 个比特进行分组(不足的补零),然后将所有分组进行按位异或运算,那么生成的结果就可以称为摘要,此算法就是一种简单的摘要算法。


如果两个输入数据经过摘要算法得到的输出摘要一致,则称为出现了哈希碰撞。一个好的摘要算法出现哈希碰撞的概率非常低,而且非常难以通过输出猜测输入的内容!计算机网络中常用的摘要算法有:MD5、SHA-1、SHA-256 等。


为了防止传输数据被黑客所篡改,发送方除了发送实际数据外,还利用摘要算法得到数据的一个摘要,并将此摘要一并发送。接收方收到数据后,利用同样的摘要算法再次得到数据的摘要,并将其与发送方发送的摘要进行比对校验,如果二者不一致,则说明数据被篡改了,反之则没有。


很容易看出来上述方式存在明显缺陷,如果黑客不仅篡改了数据,而且同时篡改了摘要,接收方不就无法判断数据是否被篡改了吗?


为了防止这种情况的发生,发送方与接收方必须有一个只有二者知道的,而黑客不能知道的东西,比如对称加密的会话密钥。不过为了提升安全性,此时一般不使用会话密钥,而是使用一个新的密钥,称之为鉴别密钥,这个密钥的获取同会话密钥。有了鉴别密钥后,摘要算法的输入就不仅仅是传输数据了,而是传输数据和鉴别密钥!黑客由于不知道鉴别密钥,就无法再伪造输入,篡改的摘要也就不正确了,从而保证了安全性!


数据和鉴别密钥级联后经过摘要算法所生成的摘要有个专用名字,称为报文鉴别码,简称 MAC。

为了进一步提升安全性,实际上客户端和服务器将使用不同的会话密钥和鉴别密钥,也就是一共需要四个密钥:
用于从客户端发送到服务器的数据的会话密钥;
用于从服务器发送到客户端的数据的会话密钥;
用于从客户端发送到服务器的数据的鉴别密钥;
用于从服务器发送到客户端的数据的鉴别密钥。


4. 数字证书

数字证书用于解决 HTTP 协议中身份容易被伪造的问题。前面提到 HTTPS 采用非对称加密算法传输会话密钥。一般是服务器将公钥对外公布,客户端利用此公钥加密会话密钥,然后服务器通过私钥解密得到会话密钥,此时双方即协商好了用于对称加密传输数据的密钥。但是万一服务器的公钥是被黑客伪造的呢?比如经典的『中间人攻击』问题:

客户端发送的请求被中间人(黑客)劫持(如使用 DNS 劫持),所有请求均发送至中间人。

中间人假装自己是正规网站(服务器),向客户端返回自己的公钥 2 ,并索要正规网站的公钥 1。

客户端使用中间人的公钥 2 加密会话密钥1,并发送至中间人。

中间人使用自己的私钥 2 解密得到会话密钥1,同时假装自己是客户端,使用正规网站的公钥 1 加密会话密钥2(可以与会话密钥 1 相同)并发送至正规网站。

客户端使用会话密钥1对数据进行加密,并发送至中间人。

中间人使用会话密钥1对数据进行解密,得到明文数据!(实现了窃听)

中间人使用会话密钥2对数据(可能是篡改的)进行加密,并发送至正规网站。

此时,客户端与服务器的通信再无安全性可言!中间人不仅能够窃听到消息内容,还能够进行篡改!


客户端如何知道自己所拥有的公钥是来自于正规网站而不是中间人呢?这时候就需要数字证书了!

数字证书的概念就像是我们的身份证一样,专门用于验证通信实体的身份。咱们的身份证是去派出所申请的,而数字证书则需要向认证中心(Certification Authority,CA)申请,而且是需要收费的!通过数字证书解决中间人攻击的具体过程为:

服务器(正规网站)首先生成一对公钥和私钥,然后将域名、申请者、公钥(注意不是私钥,私钥是无论如何也不能泄露的)等信息整合在一起,生成 .csr 文件,并将此文件发给认证中心 CA。CA 收到申请后,会通过各种手段验证申请者的信息,如无异常,则使用摘要算法得到 .csr 中明文信息的一个摘要,再用 CA 自己的私钥对这个摘要进行加密,生成一串密文,密文也称为数字签名。数字证书即包含此数字签名和 .csr 中明文信息。CA 把这个证书返回给申请人。

为了防止中间人攻击,客户端要求服务器发送其证书,并进行验证。客户端在验证证书时,把证书里的签名与及明文信息分别取出来,然后会用自身携带的 CA 机构的公钥去解密签名,得到摘要 1,再利用摘要算法得到明文信息的摘要 2,对比摘要 1 和摘要 2,如果一样,说明证书是合法的,也就是证书里的公钥是正确的,否则说明证书不合法。    



浏览器如何得到认证中心的公钥呢?万一此公钥是被伪造的呢?为了防止套娃,实际电脑操作系统中会内置这些认证中心的公钥!因而无需担心认证中心公钥被伪造的问题。Chrome 浏览器一旦发现一个网站数字证书无效,就会生成如下界面进行提示,如果用户强制访问,则存在一定的风险。


5. SSL/TLS 握手

根据前面所述,进行一下小结:

HTTPS 通过混合加密算法解决 HTTP 传输数据容易被窃听的问题,此过程需要协商会话密钥。

HTTPS 通过摘要算法解决 HTTP 传输数据容易被篡改的问题,此过程需要协商鉴别密钥。

HTTPS 通过数字证书解决 HTTP 协议中身份容易被伪造的问题,此过程需要客户端验证服务器的证书。

那么 HTTPS 具体是怎么做的呢?通信双方在什么时候协商会话密钥和鉴别密钥、什么时候验证证书合法性的呢?答案是 SSL/TLS 协议握手的时候。

HTTPS 比 HTTP 多的那个『S』就是指 SSL/TLS 协议。


在 HTTPS 协议中,当客户端与服务器通过三次握手建立 TCP 连接之后,并不会直接传输数据,而是先会经过一个 SSL/TLS 握手的过程,用于协商会话密钥、鉴别密钥以及验证证书等,之后就可以安全传输数据了!


下面通过 Wireshark 抓包,具体讲一下 SSL/TLS 1.2 四次握手的过程。


第一次握手

客户端向服务器发起加密通信请求 ,内容主要包括:

客户端支持的 SSL/TLS 协议版本,如 TLS 1.2 版本。

客户端生产的随机数 1,用于后续生成会话密钥和鉴别密钥。

客户端支持的密码套件列表,每个密码套件包含:

用于传输会话密钥的非对称加密算法,如 ECDHE、RSA;

用于验证数字证书的非对称加密算法,如 ECDHE、RSA;

用于传输数据的对称加密算法,如 AES_128_GCM、AES_128_CBC;

用于验证报文完整性的摘要算法,如 SHA256、SHA384;

格式为:TLS_非对称加密算法_非对称加密算法_对称加密算法_摘要算法,如果两个非对称加密算法一致,可省略不写。



第二次握手

服务器收到客户端加密通信请求后,向客户端发出响应,内容主要包括:

确认的 SSL/ TLS 协议版本,如果双方支持的版本不同,则关闭加密通信。

服务器生产的随机数 2,用于后续生成会话密钥和鉴别密钥。

确认的密码套件,如 TLS_RSA_WITH_AES128_CBC_SHA。

服务器的数字证书。



第三次握手

客户端收到服务器的回应之后,会验证其数字证书是否合法(验证方法在数字证书小节中有说明),如果证书合法,则进行第三次握手,内容主要包括:

客户端生产的另一个随机数 3(称为前主密钥,Pre-Master Secret,简写为 PMS),此随机数会被服务器公钥加密。

客户端根据随机数 1、随机数 2 以及前主密钥计算出主密钥(Master Secret,MS),接着将主密钥切片得到两个会话密钥和两个鉴别密钥。

加密通信算法改变通知,表示之后数据都将用会话密钥进行加密。

客户端握手结束通知,表示客户端的握手阶段已经结束。客户端会生成所有握手报文数据的摘要,并用会话密钥加密后发送给服务器,用来供服务端校验。



第四次握手

服务器收到客户端的消息后,利用自己的私钥解密出前主密钥,并根据随机数 1、随机数 2 以及前主密钥计算出主密钥,接着将主密钥切片得到两个会话密钥和两个鉴别密钥。

之后进行第四次握手,内容主要包括:

加密通信算法改变通知,表示之后数据都将用会话密钥进行加密。

服务器握手结束通知,表示服务器的握手阶段已经结束。服务器会生成所有握手报文数据的摘要,并用会话密钥加密后发送给客户端,用来供客户端校验。



至此,整个 SSL/TLS 的握手阶段全部结束!

为什么第三、第四次握手要发送所有握手报文的摘要呢?

主要原因是防止握手信息被篡改。比如客户端支持的密码套件列表中,有些加密算法较弱,有些加密算法较强,而此密码套件是明文传输的,万一黑客将此密码套件列表进行了修改,只留下一些安全性较低的加密算法,那么服务器就只能从这些安全性较低的加密算法中选择,安全性大大降低。因此需要通过发送摘要的形式防止握手信息被篡改。

为什么不直接发送一个主密钥,而是用两个随机数加一个前主密钥重新生成一个主密钥呢?

主要原因是防止连接重放。如果没有前面两个随机数,仅仅由客户端生成一个主密钥,并通过服务器公钥加密发送给服务器。那么黑客在嗅探了服务器与客户端之间的所有报文后,可以再次冒充客户端向服务器发送相同的报文(虽然黑客不知道内容是什么),因为报文信息都是之前客户端和服务器验证过的,因此服务器会认为是客户端与其通信,导致又一次连接。

假如服务器是一个购物网站,那么此连接重放会导致客户端再一次下单,造成损失。而如果有了前两个随机数,即使黑客冒充客户端想要连接重放,然而由于随机数不同,生成的密钥则不同,黑客重新发送的内容将失效(服务器不能理解、完整性摘要也不对)。

最后,用一张图总结 TLS 四次握手的过程。



随着CBC和RC4加密算法的相继“沦陷”,依赖SSL/TLS的企业需要引起高度重视,选择更安全的加密算法。糟糕的SSL实施可能会导致被实施“中间人攻击”。最近 一个来自数个安全研究学术机构成员组成的团队,又爆出了SSL的又一些漏洞。



在2013年2月, NadhemAlFardan和Kenny Paterson发现了一个名为Luck13的攻击方式, 这种攻击方式主要攻击SSL/TLS采取的CBC方式的块加密方式。攻击着可以通过阻断TLS连接的方式(比如通过恶意软件故意断掉链接,TLS会自动重发), 让发送方反复发送同一加密内容(比如cookie或者密码的密文), TLS发送错误信息的时间差异的分析。 Luck13攻击可以在较短时间内破解密文。Nadhem和AlFardan发表了他们的发现。建议在SSL/TLS中尽量不要使用CBC模式的块加密算法,可以采用如RC4这样的流加密算法。然而不到一个月,在2013年3月,NadhemAlFardan, Dan Bernstein, Kenny Paterson, Bertram Poettering 和 Jacob Schuldt又宣布,采用RC4加密的SSL/TLS也存在安全漏洞。

SSL/TLS的工作原理大致是这样的, 首先通过公钥加密算法如RSA等,通过网络交换一个共同的Session密钥。然后利用这个Session密钥利用对称加密算法进行加密。这样的好处是,由于公钥加密算法的效率较低,可以用来传递小数据量如对称加密的密钥, 然后利用对称加密的高效率来加密大数据量。CBC模式块加密和RC4的流加密都属于对称加密的算法。

根据Nadhem等人的报告,目前网络上的SSL/TLS的流量的50%大约是用RC4进行加密的。其实RC4的加密不安全性已经不是什么新闻了。其实早在2001年,两位以色列的密码专家ItsikMantin和Adi Shamir就发表了一篇论文《A practical attack on RC4》, 当时无线互联网Wi-Fi的早期加密算法WEP就是利用RC4来进行加密的。而这篇论文成为破解WEP密码的理论基础,导致了人们能够轻易的破解WEP,从而推动Wi-Fi加密全面转向WPA。(编者:有意思的是 Adi Shamir就是加密算法RSA中的“S”,而RC4中的“R”Ron Rivest,也是RSA中的“R”)。

对RC4的攻击,主要集中在RC4的随机数生成的统计不平均的漏洞上。WEP的破解就是利用WEP数据包的标号的漏洞进行统计分析从而进行破解的。而NadhemAlFardan, Dan Bernstein, Kenny Paterson, Bertram Poettering 和 Jacob Schuldt对RC4 的攻击方法,比ItsikMantin和 Adi Shamir要更进一步。他们通过对RC4的前256个密文字节的每一位建立了统计,通过224次Session就可以破解出密文的前256个字节。而研究小组还在进一步改进算法,以期达到更好的效果。

对于依赖SSL/TLS的企业来说,重要的是必须意识到SSL/TLS的安全性问题。企业需要认真检查他们的SSL安全模块的加密算法实施。对此, 研究小组给出的综合建议是:
鉴于CBC模式的块加密方式以及RC4流加密都出现了漏洞, 并且已经有了现实的攻击手法。 企业最好是在SSL/TLS加密方式上选择更为安全的AES-GCM方式。

AES-GCM方式是一种新的块加密实现方式。它可以同时实现加密和认证两个步骤。不但可以避免类似于RC4加密这样的漏洞, 同时也能够防范类似于Luck13 这样的时间差异分析的攻击手段。不过由于RC4方式的广泛使用,企业在废弃SSL/TLS的RC4方式时,也需要注意用户是否会因为服务器端不支持RC4而产生的连接问题。当然,企业需要权衡用户使用的不便与采用RC4可能导致的后果。

上文源自:IT经理网

Apache httpd 使用 Rust 重写 mod_ssl 模块

2021年2月4日,根据 ZDNet 的报道,谷歌资助了用 Rust 重写 Apache HTTP 服务器 ("httpd") 关键组件的项目。此项目由互联网安全研究小组(Internet Security Research Group,简称 ISRG)牵头,谷歌提供资金支持。被重写的关键组件是 mod_ssl 模块,用于在 Apache 服务器上建立 HTTPS 连接时进行加密,此模块依赖于 OpenSSL 提供加密引擎,OpenSSL 是 C 语言开发的加密库。

ISRG 表示,他们的计划是开发名为 mod_tls 的新模块,mod_tls 将会使用 Rust 替代 C 实现与 mod_ssl 同样的功能。此模块基于 Rustls,这是一个可作为 OpenSSL 替代方案的 Rust 开源库。ISRG 还希望他们完成工作后,httpd 能够默认使用 mod_tls,以取代目前老旧且不安全的 mod_ssl 组件。为了推进这项工作,ISRG 管理层与身为软件咨询公司 Greenbytes 创始人以及 httpd 代码提交者之一的 Stefan Eissing 签订了合同,后者将负责领导 mod_tls 项目的开发工作。

Rust 目前由 Mozilla 主导开发和提供资金支持,其设计准则为“安全、并发、实用”,特别适用于关注内存安全的场景。为了内存安全,Rust 被设计为不允许空指针和悬空指针。内存安全漏洞在安全领域一直都是严重的问题, httpd 也爆发过不少因使用 C 语言而引发的内存安全性问题。因此不难理解 ISRG 希望通过使用 Rust 重写 httpd 关键组件来提升安全性。

Rust 此前已经得到了微软、苹果、AWS、Mozilla、Linux 内核社区等组织机构的青睐,现在 Apache 软件基金会也成为了其中一员。httpd 联合创始人 Brian Behlendorf 认为 httpd 在诞生26年后,仍然是一个至关重要的基础设施。他还说道:“作为最初的联合开发者,我觉得像这样认真的改造可以为更多人提供保护,并让 httpd 在未来很长一段时间内都能发挥价值”。

Nginx关闭旧版本的TLS安全协议

Nginx disable tls1.0 and verification

这里以关闭TLSv1.0为例,对于1.1也是如法炮制。非常建议在http { } 块中修改,可以全局生效;在server块中配置只影响该服务主机。

ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # Dropping SSLv3

移除了TLSv1,加入了TLSv1.3
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;

注意:修改之前记得备份Ng的配置文件,修改保存后,使用sbin/nginx -t检测配置文件的语法,无误后重启生效。

使用nmap与ssl-enum-ciphers插件是目前最好最为直观的方法。

# nmap --script ssl-enum-ciphers -p 443 IPADDR
Starting Nmap 7.70 ( https://nmap.org ) at 2021-12-09 17:49 CST
Nmap scan report for IPADDR
Host is up (0.0018s latency).
PORT    STATE SERVICE
443/tcp open  https
| ssl-enum-ciphers:
|   TLSv1.0:
|     ciphers:
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
|     compressors:
|       NULL
|     cipher preference: server
|   TLSv1.1:
|     ciphers:
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
|     compressors:
|       NULL
|     cipher preference: server
|   TLSv1.2:
|     ciphers:
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
|     compressors:
|       NULL
|     cipher preference: server
|_  least strength: A

Nmap done: 1 IP address (1 host up) scanned in 1.26 seconds

关闭后再度检测

# nmap --script ssl-enum-ciphers -p 443 IPADDR
Starting Nmap 7.70 ( https://nmap.org ) at 2021-12-09 17:53 CST
Nmap scan report for IPADDR
Host is up (0.0020s latency).

PORT    STATE SERVICE
443/tcp open  https
| ssl-enum-ciphers:
|   TLSv1.1:
|     ciphers:
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
|     compressors:
|       NULL
|     cipher preference: server
|   TLSv1.2:
|     ciphers:
|       TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_256_GCM_SHA384 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_256_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_256_CBC_SHA (rsa 2048) - A
|       TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 (secp256r1) - A
|       TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA (secp256r1) - A
|       TLS_RSA_WITH_AES_128_GCM_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA256 (rsa 2048) - A
|       TLS_RSA_WITH_AES_128_CBC_SHA (rsa 2048) - A
|       TLS_RSA_WITH_CAMELLIA_128_CBC_SHA (rsa 2048) - A
|     compressors:
|       NULL
|     cipher preference: server
|_  least strength: A

Nmap done: 1 IP address (1 host up) scanned in 1.41 seconds

一些可选的配置项,供参考。

options-ssl-nginx.conf

# SSL configuration
listen 443 ssl default_server;
listen [::]:443 ssl default_server;

listen 443 ssl http2;
listen [::]:443 ssl http2 ipv6only=on;


ssl_session_cache shared:le_nginx_SSL:10m;
ssl_session_timeout 1440m;

ssl_session_timeout 2h;
ssl_session_tickets off;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS";

Use a stronger cipher suite

The "Old" cipher suite in Mozilla's SSL Configuration Generator is stronger than Let's Encrypt default cipher list. Mozilla's "Intermediate" list for TLS v1.2 could be a good choice:
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

Mozilla为此提供了一个专用SSL配置生产页面,很赞吧。

Mozilla's SSL Configuration Generator: a fantastic tool to generate Nginx (and Apache) SSL configurations.

重要的加强项

Disable SSL and old TLS versions(只启用Tls 1.2版本之上的协议)

ssl_protocols TLSv1.2 TLSv1.3;

Optimise cipher suites
Cipher suites are the core of TLS. This is where encryption happens.
ssl_prefer_server_ciphers on;

ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:!aNULL:!eNULL:!EXPORT:!DES::!3DES!MD5:!PSK:!RC4:!ADH:!AECDH;

Optimise SSL session cache
ssl_session_cache   shared:SSL:40m;
ssl_session_timeout 4h;

Enable session tickets
ssl_session_tickets on;

Removing 3DES from TLS(弱密钥)
Add '!3DES' to your cipher list(ssl_ciphers).

原版DES由于密钥长度过低容易被暴力破解;3DES即是设计用来提供一种相对简单的方法,即通过增加DES的密钥长度来避免类似的攻击。普遍而言,有3个独立密钥的3DES(密钥选项1)的密钥长度为168位(三个56位的DES密钥),但由于中途相遇攻击,它的有效安全性仅为112位。密钥选项2将密钥长度缩短到了112位,但该选项对特定的选择明文攻击和已知明文攻击的强度较弱,因此NIST认定它只有80位的安全性。但DES已经逐渐被更安全的AES代替。

参考来源
How to properly configure your nginx for TLS
How to remove TLSv1.0 / 1.1 and enable TLS 1.3 in Nginx


抓包工具截取 HTTPS 数据的原理

问题的场景是这样的:客户端通过浏览器向服务端发起 HTTPS 请求时,被「假基站」转发到了一个「中间人服务器」,于是客户端是和「中间人服务器」完成了 TLS 握手,然后这个「中间人服务器」再与真正的服务端完成 TLS 握手。


具体过程如下:

客户端向服务端发起 HTTPS 建立连接请求时,然后被「假基站」转发到了一个「中间人服务器」,接着中间人向服务端发起 HTTPS 建立连接请求,此时客户端与中间人进行 TLS 握手,中间人与服务端进行 TLS 握手;
在客户端与中间人进行 TLS 握手过程中,中间人会发送自己的公钥证书给客户端,客户端验证证书的真伪,然后从证书拿到公钥,并生成一个随机数,用公钥加密随机数发送给中间人,中间人使用私钥解密,得到随机数,此时双方都有随机数,然后通过算法生成对称加密密钥(A),后续客户端与中间人通信就用这个对称加密密钥来加密数据了。
在中间人与服务端进行 TLS 握手过程中,服务端会发送从 CA 机构签发的公钥证书给中间人,从证书拿到公钥,并生成一个随机数,用公钥加密随机数发送给服务端,服务端使用私钥解密,得到随机数,此时双方都有随机数,然后通过算法生成对称加密密钥(B),后续中间人与服务端通信就用这个对称加密密钥来加密数据了。
后续的通信过程中,中间人用对称加密密钥(A)解密客户端的 HTTPS 请求的数据,然后用对称加密密钥(B)加密 HTTPS 请求后,转发给服务端,接着服务端发送 HTTPS 响应数据给中间人,中间人用对称加密密钥(B)解密 HTTPS 响应数据,然后再用对称加密密钥(A)加密后,转发给客户端。

从客户端的角度看,其实并不知道网络中存在中间人服务器这个角色。

那么中间人就可以解开浏览器发起的 HTTPS 请求里的数据,也可以解开服务端响应给浏览器的 HTTPS 响应数据。相当于,中间人能够 “偷看” 浏览器与服务端之间的 HTTPS 请求和响应的数据。

但是要发生这种场景是有前提的,前提是用户点击接受了中间人服务器的证书。

中间人服务器与客户端在 TLS 握手过程中,实际上发送了自己伪造的证书给浏览器,而这个伪造的证书是能被浏览器(客户端)识别出是非法的,于是就会提醒用户该证书存在问题。


如果用户执意点击「继续浏览此网站」,相当于用户接受了中间人伪造的证书,那么后续整个 HTTPS 通信都能被中间人监听了。所以这其实并不能说 HTTPS 不够安全,毕竟浏览器都已经提示证书有问题了,如果用户坚决要访问就没有办法了。

客户端是如何验证证书的?

接下来,详细说一下实际中数字证书签发和验证流程。

如下图所示,为数字证书签发和验证流程:


当服务端向 CA 机构申请证书的时候,CA 签发证书的过程,如上图左边部分:

首先 CA 会把持有者的公钥、用途、颁发者、有效时间等信息打成一个包,然后对这些信息进行 Hash 计算,得到一个 Hash 值;
然后 CA 会使用自己的私钥将该 Hash 值加密,生成 Certificate Signature,也就是 CA 对证书做了签名;
最后将 Certificate Signature 添加在文件证书上,形成数字证书;

客户端校验服务端的数字证书的过程,如上图右边部分:

首先客户端会使用同样的 Hash 算法获取该证书的 Hash 值 H1;
通常浏览器和操作系统中集成了 CA 的公钥信息,浏览器收到证书后可以使用 CA 的公钥解密 Certificate Signature 内容,得到一个 Hash 值 H2 ;
最后比较 H1 和 H2,如果值相同,则为可信赖的证书,否则则认为证书不可信。

但事实上,证书的验证过程中还存在一个证书信任链的问题,因为我们向 CA 申请的证书一般不是根证书签发的,而是由中间证书签发的,比如百度的证书,从下图你可以看到,证书的层级有三级:


对于这种三级层级关系的证书的验证过程如下:

客户端收到 baidu.com 的证书后,发现这个证书的签发者不是根证书,就无法根据本地已有的根证书中的公钥去验证 baidu.com 证书是否可信。于是客户端根据 baidu.com 证书中的签发者,找到该证书的颁发机构是 “GlobalSign Organization Validation CA - SHA256 - G2”,然后向 CA 请求该中间证书。

请求到证书后发现 “GlobalSign Organization Validation CA - SHA256 - G2” 证书是由 “GlobalSign Root CA” 签发的,由于 “GlobalSign Root CA” 没有再上级签发机构,说明它是根证书,也就是自签证书。应用软件会检查此证书有否已预载于根证书清单上,如果有,则可以利用根证书中的公钥去验证 “GlobalSign Organization Validation CA - SHA256 - G2” 证书,如果发现验证通过,就认为该中间证书是可信的。

“GlobalSign Organization Validation CA - SHA256 - G2” 证书被信任后,可以使用 “GlobalSign Organization Validation CA - SHA256 - G2” 证书中的公钥去验证 baidu.com 证书的可信性,如果验证通过,就可以信任 baidu.com 证书。

在这四个步骤中,最开始客户端只信任根证书 GlobalSign Root CA 证书的,然后 “GlobalSign Root CA” 证书信任 “GlobalSign Organization Validation CA - SHA256 - G2” 证书,而 “GlobalSign Organization Validation CA - SHA256 - G2” 证书又信任 baidu.com 证书,于是客户端也信任 baidu.com 证书。总括来说,由于用户信任 GlobalSign,所以由 GlobalSign 所担保的 baidu.com 可以被信任,另外由于用户信任操作系统或浏览器的软件商,所以由软件商预载了根证书的 GlobalSign 都可被信任。


操作系统里一般都会内置一些根证书,比如我的 MAC 电脑里内置的根证书有这么多:


这样的一层层地验证就构成了一条信任链路,整个证书信任链验证流程如下图所示:


如果你的电脑中毒了,被恶意导入了中间人的根证书,那么在验证中间人的证书的时候,由于你操作系统信任了中间人的根证书,那么等同于中间人的证书是合法的。这种情况下,浏览器是不会弹出证书存在问题的风险提醒的。这其实也不关 HTTPS 的事情,是电脑中毒了才导致 HTTPS 数据被中间人劫持的。所以 HTTPS 协议本身到目前为止还是没有任何漏洞的,即使你成功进行中间人攻击,本质上是利用了客户端的漏洞(用户点击继续访问或者被恶意导入伪造的根证书),并不是 HTTPS 不够安全。

为什么抓包工具能截取 HTTPS 数据?

抓包工具 Fiddler 之所以可以明文看到 HTTPS 数据,工作原理与中间人一致的。对于 HTTPS 连接来说,中间人要满足以下两点,才能实现真正的明文代理:
中间人,作为客户端与真实服务端建立连接这一步不会有问题,因为服务端不会校验客户端的身份;
中间人,作为服务端与真实客户端建立连接,这里会有客户端信任服务端的问题,也就是服务端必须有对应域名的私钥;

中间人要拿到私钥只能通过如下方式:
去网站服务端拿到私钥;
去CA处拿域名签发私钥;
自己签发证书,且被浏览器信任;

不用解释,抓包工具只能使用第三种方式取得中间人的身份。

使用抓包工具进行 HTTPS 抓包的时候,需要在客户端安装 Fiddler 的根证书,这里实际上起认证中心(CA)的作用。Fiddler 能够抓包的关键是客户端会往系统受信任的根证书列表中导入 Fiddler 生成的证书,而这个证书会被浏览器信任,也就是 Fiddler 给自己创建了一个认证中心 CA。客户端拿着中间人签发的证书去中间人自己的 CA 去认证,当然认为这个证书是有效的。

如何避免被中间人抓取数据?

我们要保证自己电脑的安全,不要被病毒乘虚而入,而且也不要点击任何证书非法的网站,这样 HTTPS 数据就不会被中间人截取到了。

当然,我们还可以通过 HTTPS 双向认证来避免这种问题。一般我们的 HTTPS 是单向认证,客户端只会验证了服务端的身份,但是服务端并不会验证客户端的身份。如果用了双向认证方式,不仅客户端会验证服务端的身份,而且服务端也会验证客户端的身份。


服务端一旦验证到请求自己的客户端为不可信任的,服务端就拒绝继续通信,客户端如果发现服务端为不可信任的,那么也中止通信。

城会玩:SSL/TLS 证书最长有效期缩短至 47 天

苹果此前向 CA/B 论坛(负责管理 SSL/TLS 证书的行业组织)提议,将所有证书有效期缩短至 45 天。2025年4月的 CA/B 论坛服务器证书工作组投票通过 SC-081v3 提案,最终决定将 SSL/TLS 证书有效期从 398 天降至 47 天,SAN 数据重用周期缩短至 10 天。

SSL/TLS 证书是一种用于网站的安全协议,通过加密网络连接确保数据传输的安全性。此前 SSL/TLS 证书最长有效期可达 8 年,经过多次调整,目前最长为 398 天,开发者和企业必须在这个时间内更新一次数字证书。苹果给出的理由也非常简单,有效期缩短后,即便证书泄露也很快就会过期而不会被长时间利用。

此次投票中,证书颁发机构(CA)25 票赞成、0 票反对、5 票弃权;证书消费者(包括苹果、谷歌、微软、Mozilla 主要浏览器开发商)4 票赞成、0 票反对、0 票弃权。提案通过后,将进入知识产权审查阶段。该措施将从 2026 年 3 月开始逐步实施,到 2029 年 3 月结束,具体时间表如下:
2026 年 3 月 14 日前:证书有效期最长为 398 天
2027 年 3 月 14 日前:证书有效期最长缩短至 200 天
2028 年 3 月 14 日前:证书有效期最长缩短至 100 天
2028 年 3 月 15 日后:证书有效期最长缩短至 47 天

尽管 SSL/TLS 证书已经有很多便捷的工具可以实现自动化续签,但并非每个网站和企业都可以轻松部署自动化续签流程,尤其是有些复杂的系统切换数字证书本身就是个麻烦的事情。在 Reddit 论坛上有数百名系统管理员抱怨苹果的这项提议,因为缩短证书有效期后剩余的工作都要系统管理员承担,尤其是如果管理多个网站那么工作量将会显著提升 (假如无法实现自动化续签)。