SSL如何防止被黑客破坏?Apache证书配置的新方法

在早年,Apache官方服务器网站曾被国内知名网络黑客安全专家郭盛华发现严重漏洞。Apache的错误证书,对于众多技术人员来说,这些复杂性的最新来源是公钥基础结构(PKI),它是用于维护网络上安全连接的安全性基础结构,如果其他任何人在Windows中遇到相同的看似莫名其妙的行为,我也想在这里重述一下我的经验。他们的PKI配置。这些天来与运行您自己的Web服务器相关的复杂性给我留下了深刻的印象。

         

对我而言,所有这一切都始于以下想法:在尝试了数年的尝试后,现在是时候升级我的Web服务器系统,使其包含重写规则以推动所有HTTP(未加密)请求使用传输层安全性来使用安全会话了(TLS)。这不仅仅是时间,这远远超出了时间!

同时,我认为最好升级基础Web服务器硬件以更新Web平台以满足当前的流量需求。

因此,我开始了将整个Web服务环境迁移到运行最新版本软件集的不同硬件平台的路径。所有值得称赞的目标,所以我想!

该决定意味着必须改变服务的许多方面。以前平台上的操作系统停留在FreeBSD 11.2上,当前发行版本是12.1。我的托管框架在Apache 2.4配置上使用虚拟托管。旧主机使用Apache版本2.4.35,而新系统使用Apache版本2.4.41。在旧主机上,所有虚拟主机都被加载到一个监听所有接口的Apache实例中。这次围绕虚拟主机分配了两个Apache实例,每个实例侦听不同的IP地址,以便在平台中执行某种级别的负载隔离。

在此过渡过程中,有许多活动的部分,在执行迁移的每个步骤时,我们都会仔细检查新服务的完整性。

让我们加密证书

这里的问题之一是确保未破坏SSL配置。除了拆分虚拟主机定义外,此步骤还更改了Apache中的证书声明。旧系统使用了以下配置:

SSLCertificateFile“ /dir/cert.pem” SSLCertificateChainFile“ /dir/fullchain.pem”

看来这些天我们应该使用稍微不同的模板:

SSLCertificateFile“ /dir/cert.pem” SSLCertificateChainFile“ /dir/chain.pem”

区别在于,Let's Encrypt的“全链”证书同时具有由Digital Signature Trust Co.颁发的对Let's Encrypt进行认证的证书和由Let's的Encrypt进行我的域认证的证书,而“ chain”的证书仅具有父证书由Digital Signature Trust发布。

从理论上讲这并不重要,但是我们还是决定使用更新后的模板。我们安装了软件,运行了测试(curl中的-resolve选项在实际切换整个环境之前,对测试设置非常有用),然后更改了DNS,并在新的Web平台上开始了进一步的测试。

使用各种浏览器访问重定位的服务在更改后的配置文件中存在一些打字错误的问题。但是,一旦解决了这些问题,一切看起来都很好。

除了一项测试。

OpenSSL

我们使用的测试是使用OpenSSL的客户端连接。命令是:

$ openssl s_client -connect x.labs.apnic.net:443

输出量很大,但是这里值得关注的部分是证书链

$ openssl s_client -connect x.labs.apnic.net:443 CONNECTED(00000003) depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 verify return:1 depth=0 CN = y.labs.apnic.net verify return:1 --- Certificate chain 0 s:/CN=y.labs.apnic.net i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 1 s:/CN=y.labs.apnic.net i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 2 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 i:/O=Digital Signature Trust Co./CN=DST Root CA X3 ---

这里有些奇怪。我们正在尝试通过端口443 通过x.labs.apnic.net连接到虚拟主机,但是提供用于建立连接的证书却完全不同。这是主机名y.labs.apnic.net的证书。这不是一个完全随机的选择,因为在Apache Virtual Host配置中还定义了一个虚拟主机y.labs.apnic.net。

为什么当我们要求OpenSSL客户端连接到x.labs.apnic.net时,我们看到Apache服务器为y.labs.apnic.net提供证书?

试图了解这种情况的第一步是在不同的主机上尝试相同的openssl s_client命令,而其他一些主机复制了上面显示的相同的异常响应,而其他主机的行为与我们预期的一样:

$ openssl s_client -connect x.labs.apnic.net:443 CONNECTED(00000003) depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 verify return:1 depth=0 CN = x.labs.apnic.net verify return:1 --- Certificate chain 0 s:CN = x.labs.apnic.net i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 1 s:CN = x.labs.apnic.net i:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 2 s:C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 i:O = Digital Signature Trust Co., CN = DST Root CA X3 ---

我们期望的是cert链中的证书0和1:x.labs.apnic.net中的主题名称。显然,这是一致的行为。我们使用的每个主机都显示了其中一种其他行为,但每个测试主机都没有在这些行为之间切换。它要么使用了预期的证书,要么使用了不同的证书来建立SSL会话,并且反复进行。

此时有两个问题:

为什么为某些测试主机而不是全部提供y.labs.apnic.net证书??

为什么不为这些主机提供x.labs.apnic.net证书?

为什么要提交y.labs.apnic.net证书?

可以通过仔细阅读Apache文档来回答第一个问题。可以在基于名称的虚拟主机的Apache Wiki上找到最佳解释。

在初始HTTP请求中使用HTTP Host:参数支持HTTP中的虚拟主机。这是此类连接的转储:

* Connected to x.labs.apnic.net (2401:2000:6660::109) port 80 (#0) > GET / HTTP/1.1 > Host: x.labs.apnic.net > User-Agent: curl/7.64.1 > Accept: */* > < HTTP/1.1 200 OK ... ...

与端口80 Apache服务器的初始TCP连接对于所有虚拟主机都是通用的,并且它是GET指令的Host:部分,用于建立会话的预期虚拟主机参数。

此方法不适用于通过端口443的SSL连接。必须先建立安全会话,然后 才能建立 HTTP连接,因此在客户端可以在HTTP内部发出Host指令之前,Apache服务器需要告知要使用的证书。会议。

在TLS中,这可以通过使用初始TLS握手(RFC 4366)中的服务器名称指示(SNI)字段来实现。客户端在初始未加密的交换中告诉服务器它要连接的服务器的名称,以便服务器可以选择“正确的”证书用于响应。

是的,初始TLS交换中的未加密SNI字段是一个信息泄漏,并且已采取措施在TLS 1.3中对该字段进行加密,但这在不同的时间是不同的情况。

如果TLS握手的ClientHello部分中没有SNI字段怎么办?

如果您已打开Apache的指令SSLStrictSNIVHostCheck,则没有SNI表示将不建立连接。但是我什至不知道存在这样的指令,而且我当然没有将其设置为“ on”。我使用的是默认操作,在Apache的情况下,TLS握手中缺少SNI字段会导致Apache使用配置集中的第一个虚拟主机进行响应。在我的配置中是y.labs.apnic.net。当我将配置更改为使用其他初始虚拟主机时,openssl s_client命令提示服务器将其不同的证书传回。

这足以得出我们对第一个问题有答案的结论。所述y.labs.apnic.net证书被使用,因为OpenSSL的的s_client.First命令没有包括SNI值。这触发了我的Apache服务器在其本地配置中使用第一个虚拟主机上下文来完成TLS握手,因为我没有启用严格的SNI检查,因此使用了Apache的默认操作。这恰好是y.labs.apnic.net的条目。

这是Apache SSL配置部分中的第一个虚拟主机块,当TLS设置的ClientHello部分中未提供SNI值时,我们将使用默认的Apache行为来选择第一个虚拟主机上下文。

为什么不显示x.labs.apnic.net证书?

最近的客户端浏览器总是提供SNI值,而curl总是提供SNI值,因此当我使用其他诊断工具(例如curl)时,一切看起来都很好。

* Trying 2401:2000:6660::109… * TCP_NODELAY set * Trying 203.133.248.109… * TCP_NODELAY set * Connected to x.labs.apnic.net (2401:2000:6660::109) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * Cipher selection: ALL:!EXPORT:!EXPORT40:!EXPORT56:!aNULL:!LOW:!RC4:@STRENGTH * successfully set certificate verify locations: * CAfile: /etc/ssl/certs/ca-certificates.crt CApath: /etc/ssl/certs * TLSv1.2 (OUT), TLS header, Certificate Status (22): * TLSv1.2 (OUT), TLS handshake, Client hello (1): * TLSv1.2 (IN), TLS handshake, Server hello (2): * TLSv1.2 (IN), TLS handshake, Certificate (11): * TLSv1.2 (IN), TLS handshake, Server key exchange (12): * TLSv1.2 (IN), TLS handshake, Server finished (14): * TLSv1.2 (OUT), TLS handshake, Client key exchange (16): * TLSv1.2 (OUT), TLS change cipher, Client hello (1): * TLSv1.2 (OUT), TLS handshake, Finished (20): * TLSv1.2 (IN), TLS change cipher, Client hello (1): * TLSv1.2 (IN), TLS handshake, Finished (20): * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384 * ALPN, server accepted to use http/1.1 * Server certificate: * subject: CN=x.labs.apnic.net * start date: Apr 3 21:03:30 2020 GMT * expire date: Jul 2 21:03:30 2020 GMT * subjectAltName: host "x.labs.apnic.net" matched cert's "x.labs.apnic.net" * issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3 * SSL certificate verify ok. > GET / HTTP/1.1 > Host: x.labs.apnic.net > User-Agent: curl/7.52.1 > Accept: */* > < HTTP/1.1 200 OK

此连接是在openssl s_client命令出现问题的平台上建立的。如笔录所示,该连接使用TLS 1.2版打开。

这意味着问题似乎与openssl s_client命令有关。

当我们使用openssl s_client命令按预期工作的主机时会发生什么?

* TCP_NODELAY set * Expire in 149999 ms for 3 (transfer 0x555f219aaf50) * Connected to x.labs.apnic.net (2401:2000:6660::109) port 443 (#0) * ALPN, offering h2 * ALPN, offering http/1.1 * successfully set certificate verify locations: * CAfile: none CApath: /etc/ssl/certs * TLSv1.3 (OUT), TLS handshake, Client hello (1): * TLSv1.3 (IN), TLS handshake, Server hello (2): * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): * TLSv1.3 (IN), TLS handshake, Certificate (11): * TLSv1.3 (IN), TLS handshake, CERT verify (15): * TLSv1.3 (IN), TLS handshake, Finished (20): * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): * TLSv1.3 (OUT), TLS handshake, Finished (20): * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 * ALPN, server accepted to use http/1.1 * Server certificate: * subject: CN=x.labs.apnic.net * start date: Apr 3 21:03:30 2020 GMT * expire date: Jul 2 21:03:30 2020 GMT * subjectAltName: host "x.labs.apnic.net" matched cert's "x.labs.apnic.net" * issuer: C=US; O=Let's Encrypt; CN=Let's Encrypt Authority X3 * SSL certificate verify ok. > GET / HTTP/1.1 > Host: x.labs.apnic.net > User-Agent: curl/7.64.0 > Accept: */* > * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): * old SSL session ID is stale, removing < HTTP/1.1 200 OK

几乎相同,但是现在会话使用的是TLS版本1.3。这些主机上的OpenSSL库版本似乎有问题。

Failing hosts: $ openssl version OpenSSL 1.1.0l 10 Sep 2019 Working hosts: $ openssl version OpenSSL 1.1.1d 10 Sep 2019

似乎OpenSSL在1.1.0和1.1.1之间有所更改。

让我们看一下OpenSSL 1.1.1的文档 -以及s_client文档,因为OpenSSL是加密功能的“德克萨斯电锯杀人狂”,并且该库具有大量命令和选项!

-servername名称

将ClientHello消息中的TLS SNI(服务器名称指示)扩展名设置为给定值。如果未提供-servername,则TLS SNI扩展名将遵循DNS名称格式填充给-connect的名称。如果也未提供-connect,则SNI设置为“ localhost”。这是自OpenSSL 1.1.1以来的默认设置。

即使SNI通常应该是DNS名称而不是IP地址,但如果提供了-servername,则无论该名称是否是DNS名称,都将发送该名称。

嗯 如果这是自OpenSSL 1.1.1起的默认设置,那么该版本之前的默认设置是什么?这是与OpenSSL s_client版本1.1.0相同的手册条目。

-servername名称

在ClientHello消息中设置TLS SNI(服务器名称指示)扩展名。

现在的含义很明显:如果未使用此伪指令设置TLS SNI,则ClientHello消息中没有SNI扩展名。

看来我们已经找到了问题。让我们检查一个OpenSSL 1.1.0客户端,为servername参数添加一个值:

$ openssl s_client -connect x.labs.apnic.net:443 -servername x.labs.apnic.net CONNECTED(00000003) depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 verify return:1 depth=0 CN = x.labs.apnic.net verify return:1 --- Certificate chain 0 s:/CN=x.labs.apnic.net i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 1 s:/CN=x.labs.apnic.net i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 2 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 i:/O=Digital Signature Trust Co./CN=DST Root CA X3 --- ---

让我们通过提供与连接名称参数不同的servername参数值来确认:

$ openssl s_client -connect x.labs.apnic.net:443 -servername dmm.labs.apnic.net CONNECTED(00000003) depth=2 O = Digital Signature Trust Co., CN = DST Root CA X3 verify return:1 depth=1 C = US, O = Let's Encrypt, CN = Let's Encrypt Authority X3 verify return:1 depth=0 CN = dmm.labs.apnic.net verify return:1 --- Certificate chain 0 s:/CN=dmm.labs.apnic.net i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3 i:/O=Digital Signature Trust Co./CN=DST Root CA X3 ---

现在,我们对第二个问题有了答案。这就是我们在具有OpenSSL版本1.1.0的主机上使用openssl -s_client的方式。通过省略-servername参数,我们触发了此行为。

正确的证书

因此,我们已经找到了问题。Apache没有错,Let's Encrypt证书没有错,浏览器没有错。OpenSSL中甚至没有任何“错误”。

但是,跨版本的OpenSSL库中的诊断工具之一的默认行为存在细微的不一致。

安全旨在帮助我们构建更强大的系统。但是使用PKI,我们拥有数量惊人的活动部件,现在即使在每个组件中,不同版本的诊断工具也可能具有不同的默认行为。

我们经常听到“安全很难”。但是,从推动和推动Web服务器配置以及浏览搜索结果这一天开始,我的收获与众不同。如今,“安全性已变得晦涩难懂”,这是不应该发生的事情!(欢迎转载分享)

原文地址:https://www.cnblogs.com/hacker520/p/12687282.html