尽管连接字符串中有 TrustServerCertificate=true,但 SSL / 证书验证错误

SSL / certificate validation error in spite of TrustServerCertificate=true in connection string

首先请注意,我知道这个问题已经被问过好几次了。但是,到目前为止给出的已接受(和未接受)的解决方案适用于我的情况,因此从那时起一定发生了实质性的变化,希望有理由再次询问。

话虽如此:

我目前正在尝试将 Access 2010 .adp 应用程序升级到 Access 2019 .accdb。该应用程序包含大量 VBA 代码,这些代码使用 ADO 对象连接并在 Microsoft SQL 服务器(当前:2008 R2,但将很快升级)上运行。

我想保留大部分代码,这意味着坚持使用 ADO,所以要走的路是新的 OleDB SQL 服务器驱动程序(在2018)。 SQL 服务器在我的客户端应用程序之外的另一台机器上运行。

我无法从 VBA 建立到 SQL 服务器的连接。执行以下代码片段时

Dim cnTemp As Connection
Set cnTemp = New Connection
cnTemp.CursorLocation = adUseServer
cntemp.Open "Provider=MSOLEDBSQL;Server=dbserver.example.com;Initial Catalog=MyDB;Authentication=SqlPassword;User ID=sa;Password=secret;DataTypeCompatibility=80;"

执行最后一行时出现以下错误:

SSL Provider: The certificate chain was issued by an authority which is not trusted.

好的,没问题,毕竟我们已经找到了处理同一问题的所有其他问题,所有这些问题都提出了相同的解决方案:将 Trust Server Certificate=True; 添加到连接字符串。

好吧,试过了,但令我惊讶的是,情况还是一样。然后我尝试了一些其他变体,如 TrustServerCertificate=True; 或使用 true 而不是 True,但无济于事。我还尝试添加 Use Encryption for Data=True; 但这也没有帮助(这是可以预料的)。此外,我尝试了一些在研究问题时发现的片段,但 Microsoft 未将其记录为在 ADO 连接字符串中有效(如 Encrypt=trueTrusted_Connection=true;);当然,这使情况变得更糟,引发了其他错误消息。

我明白我可以通过将 SQL 服务器证书放入客户端的受信任的根证书库,或者让 SQL 服务器使用由已知的、受信任的 CA(例如 Let's Encrypt)。

但是,我非常想知道为什么将 Trust Server Certificate=true; 添加到我的连接字符串不会使错误消失,以及我必须在其中放置什么来禁用证书验证(顺便说一句,如果我们不开始讨论为什么这会很糟糕,我将不胜感激;这只是在受信任的封闭网络中进行开发和测试,我知道可能存在的风险)。

连接字符串中的 TrustServerCertificate=True 不被接受的原因有两个。一是它不是有效的 ADO 经典 (ADODB) 连接字符串关键字。根据ActiveX Data Objects (ADO) Connection String Keywords documentation,keyword/value对应该是Trust Server Certificate=True(注意空格)。关键字在没有空格的情况下被完全忽略,因此不被信任。

但是,由于 Authentication-SqlPassword 规范,仅此更改不会信任证书。当指定 Authentication 关键字时,文档脚注会调用:

To improve security, encryption and certificate validation behavior is modified when using Authentication/Access Token initialization properties or their corresponding connection string keywords. For details, see Encryption and certificate validation link.

引用的 link 包含以下重要说明:

Certificate validation can also be controlled through the Value field of the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SNI18.0\GeneralFlags\Flag2 registry entry. Valid values are 0 or 1. The OLE DB driver chooses the most secure option between the registry and the connection property/keyword settings. That is, the driver will validate the server certificate as long as at least one of the registry/connection settings enables server certificate validation.

因此,即使使用 Trust Server Certificate=True,当此注册表值设置为 0 时,证书也会被验证。

一个解决方案是简单地删除 Authentication=SqlPassword 规范,只要您不需要通过不信任服务器证书来提高安全性:

cntemp.Open "Provider=MSOLEDBSQL;Server=dbserver.example.com;Initial Catalog=MyDB;User ID=sa;Password=secret;Trust Server Certificate=True;DataTypeCompatibility=80;"

首先,我想声明所有功劳归功于@Dan Guzman。是他的回答/评论提供了解决方案。

但是,我想根据发布问题后所做的研究添加一些背景知识。

问题是微软的文档明显有误。请查看以下文档:

https://docs.microsoft.com/en-us/sql/connect/oledb/applications/using-connection-string-keywords-with-oledb-driver-for-sql-server?view=sql-server-2017#table3_1

位于SQL Server 2017 -> OLE DB -> Applications -> Using connection string keywords with OLE DB Driver for SQL server版块,应该是正确的。它分为三个部分;在这个问题的上下文中,最后一个 table 是我们感兴趣的,因为只有这个与 ADO 的连接字符串有关。

最后的 table 明确表明 Authentication=SqlPawword 在 ADO / OLE DB 连接字符串中有效(重新格式化我的,内容没有改变):

Authentication SSPROP_AUTH_MODE Specifies the SQL or Active Directory authentication used. Valid values are:

(not set): Authentication mode determined by other keywords. ActiveDirectoryPassword: Active Directory authentication using login ID and password.

ActiveDirectoryIntegrated: Integrated authentication to Active Directory using the currently logged-in user's Windows account credentials.

NOTE: It's recommended that applications using Integrated Security (or Trusted_Connection) authentication keywords or their corresponding properties set the value of the Authentication keyword (or its corresponding property) to ActiveDirectoryIntegrated to enable new encryption and certificate validation behavior.

SqlPassword: Authentication using login ID and password.

NOTE: It's recommended that applications using SQL Server authentication set the value of the Authentication keyword (or its corresponding property) to SqlPassword to enable new encryption and certificate validation behavior.

它还说(同样,格式化我的,内容没有改变):

Trust Server Certificate SSPROP_INIT_TRUST_SERVER_CERTIFICATE Accepts the strings "true" and "false" as values. The default value is "false", which means that the server certificate will be validated.

每个通情达理的人都会理解这一点,因为 Trust Server Certificate=true 将禁用证书验证。

但是当你看到这里时

https://docs.microsoft.com/en-us/sql/relational-databases/native-client/applications/using-connection-string-keywords-with-sql-server-native-client?view=sql-server-2017

您会注意到此文档的结构与第一个文档类似,最后一个 table 没有 提及 Authentication 参数。

但是,此文档位于 SQL Server 2017 -> Development -> SQL Server Native Client -> Applications -> Using Connection String Keywords。这意味着它与我们的案例无关,因为它涉及 SQL 服务器本机客户端(而不是 OLE DB),但它提供了正确的信息。

所以我们有提供错误信息的正确文档和提供正确信息的不相关文档。恭喜你,微软,你又让我浪费了一整天...

此外,我还找到了以下文档:

https://docs.microsoft.com/en-us/sql/connect/oledb/features/using-azure-active-directory?view=sql-server-2017#encryption-and-certificate-validation

阅读标题("Using Azure Active Directory"),它应该仅与 Azure 有关。但是,我怀疑以下部分也与本地 SQL 服务器安装有关(格式化我的,内容未更改):

Certificate validation

To improve security, the new connection properties/keywords respect the TrustServerCertificate setting (and its corresponding connection string keywords/properties) independently of the client encryption setting. As a result, server certificate is validated by default.

Note

Certificate validation can also be controlled through the Value field of the HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSSQLServer\Client\SNI18.0\GeneralFlags\Flag2 registry entry. Valid values are 0 or 1. The OLE DB driver chooses the most secure option between the registry and the connection property/keyword settings. That is, the driver will validate the server certificate as long as at least one of the registry/connection settings enables server certificate validation.

因此很可能我们还必须更改注册表中的值以在通过 ADO / OLE DB 连接到 SQL 服务器时最终禁用证书验证。