为什么 PHP/OpenSSL 拒绝这个通配符 TLS 证书?

Why is PHP/OpenSSL rejecting this wildcard TLS certificate?

我正在尝试使用 PHPMailer 通过基于 TLS 的 SMTP 发送电子邮件。

我发现当 PHPMailer 尝试连接到 SMTP 服务器并调用 STARTTLS 时,连接立即失败 - 除非我在打开时设置 PHP SSL context variable verify_peer_name => false SMTP 连接。

我要连接的 SMTP 服务器是 prefix.myredactedcompany.mailguard.com.au:2525。查看 SMTP 日志,我看到当我请求连接到 prefix.myredactedcompany.mailguard.com.au:2525 时,我实际上连接到了 someotherhost.mailguard.com.au。我假设这是负载平衡器或其他集群设置的结果。

当我按照 PHPMailer 故障排除指南 test the OpenSSL connection outside of PHP 通过 运行 命令 echo QUIT | .\openssl.exe s_client -starttls smtp -crlf -connect prefix.myredactedcompany.mailguard.com.au:2525 时,我得到以下结果:

CONNECTED(000001E0)
---
Certificate chain
 0 s:/C=AU/postalCode=3006/ST=VIC/L=SOUTHBANK/street=198 NORMANBY RD/O=MailGuard Pty Ltd/OU=Netops/OU=PremiumSSL Wildcard/CN=*.mailguard.com.au
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
 2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIF0jCCBLqgAwIBAgIQI1raes2jQvcAbHxOBIAtqTANBgkqhkiG9w0BAQsFADCB
... snip ...
pYwh4eDZtcm4tZQfc71R1KhA9ci5A0G9ewPLmZUYoDlguNdlNlVf07aus54EV6XI
1wHfJ/xs
-----END CERTIFICATE-----
subject=/C=AU/postalCode=3006/ST=VIC/L=SOUTHBANK/street=198 NORMANBY RD/O=MailGuard Pty Ltd/OU=Netops/OU=PremiumSSL Wildcard/CN=*.mailguard.com.au
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 5395 bytes and written 468 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: redacted
    Session-ID-ctx: 
    Master-Key: redacted
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 7200 (seconds)
    TLS session ticket:
    0000 - 18 ad e7 c2 c9 46 ab 96-5f 58 03 81 fc 48 3c 18   .....F.._X...H<.
    ... snip ...
    0090 - 41 47 9b f2 60 c4 41 5f-0d fc ea 2b 40 0c 25 3b   AG..`.A_...+@.%;

    Start Time: 1549441609
    Timeout   : 300 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
---

PHPMailer 故障排除指南指出在这种情况下

The verify error:num=20:unable to get local issuer certificate is not a problem.

对我来说,这看起来像是 mailguard.com.au 所有子域的有效通配符证书。为什么 PHPMailer/OpenSSL 拒绝它,除非我关闭对等名称验证?是否因为实际的 SMTP 主机名与我在建立出站连接时使用的 DNS 名称不匹配?

如果我关闭对等名称验证,我会面临哪些安全风险?

版本:


一位评论者询问直接连接到池中的其中一台主机时,openssl 连接结果如何。极其相似:

CONNECTED(000001F8)
---
Certificate chain
 0 s:/C=AU/postalCode=3006/ST=VIC/L=SOUTHBANK/street=198 NORMANBY RD/O=MailGuard Pty Ltd/OU=Netops/OU=PremiumSSL Wildcard/CN=*.mailguard.com.au
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
 1 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
   i:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
 2 s:/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIF0jCCBLqgAwIBAgIQI1raes2jQvcAbHxOBIAtqTANBgkqhkiG9w0BAQsFADCB
...snip...
pYwh4eDZtcm4tZQfc71R1KhA9ci5A0G9ewPLmZUYoDlguNdlNlVf07aus54EV6XI
1wHfJ/xs
-----END CERTIFICATE-----
subject=/C=AU/postalCode=3006/ST=VIC/L=SOUTHBANK/street=198 NORMANBY RD/O=MailGuard Pty Ltd/OU=Netops/OU=PremiumSSL Wildcard/CN=*.mailguard.com.au
issuer=/C=GB/ST=Greater Manchester/L=Salford/O=COMODO CA Limited/CN=COMODO RSA Organization Validation Secure Server CA
---
No client certificate CA names sent
Peer signing digest: SHA512
Server Temp Key: ECDH, P-256, 256 bits
---
SSL handshake has read 5399 bytes and written 468 bytes
---
New, TLSv1/SSLv3, Cipher is ECDHE-RSA-AES256-GCM-SHA384
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
SSL-Session:
    Protocol  : TLSv1.2
    Cipher    : ECDHE-RSA-AES256-GCM-SHA384
    Session-ID: redacted
    Session-ID-ctx: 
    Master-Key: redacted
    Key-Arg   : None
    PSK identity: None
    PSK identity hint: None
    SRP username: None
    TLS session ticket lifetime hint: 7200 (seconds)
    TLS session ticket:
    0000 - 6d bd 68 e7 44 29 72 ea-99 00 c8 84 a6 cc 45 76   m.h.D)r.......Ev
    ... snip ...
    0090 - 28 73 65 a4 a1 24 fd c6-18 ad fb 13 26 ec 6f b9   (se..$......&.o.

    Start Time: 1549519771
    Timeout   : 300 (sec)
    Verify return code: 20 (unable to get local issuer certificate)
---

好的,所以 php.ini 中的注释说空的 openssl.cafile 设置将导致 PHP 使用 OS 管理的证书存储。这在 Windows 上似乎不正确,因为 OpenSSL 没有对 Windows 证书存储的内置支持。因此,我按照 中的说明设置了本地根 CA 捆绑包。这似乎现在有效。当我 运行 上面给出的 OpenSSL 测试命令并附加 -CAfile C:\xampp\extras\cacert.pem 时,最终结果现在是 Verify return code: 0 (ok) 而不是 Verify return code: 20 (unable to get local issuer certificate).

我重新启动了 Apache 并检查了 phpinfo() 中 openssl.cafile 的值是否正常。但是 PHPMailer 仍然无法成功启动 STARTTLS。症状与以前相同 - 如果我将 SSL 上下文变量 verify_peer_name 设置为 false,PHPMailer 会成功连接。如果我让 verify_peer_name 保持打开状态,则在 STARTTLS 期间连接失败。

我仍然有兴趣知道:

  1. 禁用 verify_peer_name 的安全隐患是什么?我假设它会使 MITM 连接变得容易。
  2. 如何进一步诊断 STARTTLS 失败的确切原因,尤其是现在证书已通过 OpenSSL 的验证?

可能是因为通配符只在一个级别匹配,即prefix.myredactedcompany.mailguard.com.au不匹配*.mailguard.com.au。使用报告的名称(连接时看到)应该有助于解决这个问题。

或者,它可能是 PHP 使用的 CA 证书包,这意味着服务器没问题,但您无法正确验证其证书 - 这通常很难诊断,因为它可以小心地告诉你的链中验证失败的位置。例如,如果您的链是:

host cert -> intermediate cert -> root (CA) cert

其中任何一个验证失败都会导致验证失败,但可能不清楚哪个是错误的。

您可能需要获取最新 CA 证书包的副本并告诉 PHP 使用它(如 the PHPMailer troubleshooting guide), or use a package like Certainty 中所述以从您的应用程序管理它。

也可能是您的中间证书顺序错误。