TSQL CREATE ASSEMBLY FROM varbinary 破坏了 class_name 参数

Posted

技术标签:

【中文标题】TSQL CREATE ASSEMBLY FROM varbinary 破坏了 class_name 参数【英文标题】:TSQL CREATE ASSEMBLY FROM varbinary breaks the class_name parameter 【发布时间】:2019-07-24 00:50:51 【问题描述】:

我已将我的本地 SQL Server 2008 R2 数据库迁移到 Azure SQL Server 托管实例 (SQL Server 2017)。一个数据库是存档(只读)数据库,一个是 OLTP 数据库,第三个是实用程序数据库,我在其中保存通用函数、存储过程和维护脚本。除了实用程序数据库中的 CLR 程序集外,所有三个数据库的一切都进行得非常顺利。该程序集在 TSQL 代码中提供正则表达式功能 - 非常有用!我基于 Phil Factor 代码here。最初,它是从已编译的 DLL 加载到本地数据库中的。它在那里像冠军一样工作。但在 SQL MI 上,运行使用 CLR 函数之一的 SP 时出现以下错误。

消息 10314,级别 16,状态 11,过程 dbo.globalSearch,第 22 行 [批处理开始第 2 行] 尝试加载程序集 id 65541 时,Microsoft .NET Framework 发生错误。服务器可能资源不足,或者程序集可能不受信任。再次运行查询,或查看文档以了解如何解决程序集信任问题。有关此错误的详细信息: System.IO.FileLoadException:无法加载文件或程序集“regexsqlclr,Version=0.0.0.0,Culture=neutral,PublicKeyToken=null”或其依赖项之一。发生与安全有关的错误。 (HRESULT 异常:0x8013150A) System.IO.FileLoadException: 在 System.Reflection.RuntimeAssembly._nLoad(AssemblyName 文件名、字符串代码库、证据 assemblySecurity、RuntimeAssembly locationHint、StackCrawlMark 和 stackMark、IntPtr pPrivHostBinder、布尔 throwOnFileNotFound、布尔 forIntrospection、布尔suppressSecurityChecks) 在 System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef,证据 assemblySecurity,RuntimeAssembly reqAssembly,StackCrawlMark 和 stackMark,IntPtr pPrivHostBinder,布尔 throwOnFileNotFound,布尔 forIntrospection,布尔suppressSecurityChecks) 在 System.Reflection.RuntimeAssembly.InternalLoad(字符串 assemblyString,证据 assemblySecurity,StackCrawlMark 和 stackMark,IntPtr pPrivHostBinder,Boolean forIntrospection) 在 System.Reflection.RuntimeAssembly.InternalLoad(字符串 assemblyString,证据 assemblySecurity,StackCrawlMark 和 stackMark,Boolean for Introspection) 在 System.Reflection.Assembly.Load(String assemblyString)

我已经尝试使用this MSDN 帖子中的步骤解决程序集信任问题,具体执行

sys.sp_add_trusted_assembly

成功了,但什么也没改变。然后我想,既然它暗示它不能从文件中加载,这 似乎 是有道理的,因为该文件在我无法访问文件系统的 SQL MI 中不存在,我应该尝试从varbinary 删除和重新创建。我只是说它似乎是有道理的,因为除了我最初从中加载它的服务器之外,该文件也不存在于我的任何其他本地服务器上,并且它在所有服务器上都能完美运行.但是,我愿意尝试任何事情!因此,我使用 SSMS 将程序集编写为 DROPCREATE,它使用 FROM BINARY 语法,并同样编写了所有函数的脚本。 CREATE ASSEMBLY 成功了,所以我认为我在正确的轨道上。然后我尝试创建第一个函数和BAM,另一个错误!这次错误读取

消息 6505,级别 16,状态 2,过程 RegexIndex,第 2 行 在程序集“RegexFunctions”中找不到类型“RegexSQLCLR.RegularExpressionFunctions”。

我已经在谷歌上搜索了几个小时,试图找到解决该问题的方法,但运气却为零。尽管 EXTERNAL NAME 子句的类部分的语法对于从文件加载的程序集非常有效。我验证了由 SSMS 编写的 varbinary 与原始 DLL 的二进制文件相同。 Microsoft 论坛上的某个人建议我确保使用 Any CPU 选项编译 DLL - 确实如此。作为健全性检查,我在其中一台本地服务器(即DROPCREATE ASSEMBLY FROM BINARY)上执行了相同的过程,得到了完全相同的结果:我无法加载任何 CLR 函数!我已经尝试了我能想到的所有可能的类名排列,但无济于事。这是CREATE ASSEMBLYCREATE FUNCTION 的代码

CREATE ASSEMBLY [RegexFunction]
AUTHORIZATION [dbo]
FROM 0x4D5A90000 *truncated_for_brevity*
WITH PERMISSION_SET = SAFE

CREATE FUNCTION RegExIndex
   (
    @Pattern NVARCHAR(4000),
    @Input NVARCHAR(MAX),
    @Options int
   )
RETURNS int
AS EXTERNAL NAME 
   RegexFunction.[RegexSQLCLR.RegularExpressionFunctions].RegExIndex
GO

RegexSQLCLR 是原始 DLL 的名称,RegularExpressionFunctions 是类的名称。 RegexSQLCLR也是使用CREATE ASSEMBLY FROM BINARY后在sys.assembly_filesname列中指定的名称;否则,原始 DLL 的完整路径位于 name 列中。

【问题讨论】:

当我们试图解决这个问题时,您为什么不尝试安装我创建的 SQLCLR 库SQL#,因为它包含相当多的 RegEx 函数和许多其他内容。您不需要所有程序集,因此您可以在标题注释下方的部分中将一些设置为“0”(例如NetworkOSTwitterizer)和@MaxAllowedAccessLevel = 1。跨度> @SolomonRutzky 谢谢,我会看看它。顺便说一句,我确实找到了答案(见下文)。这两天我再也回不来了!至少这使得最初的 CLR 工作。但我仍然很好奇为什么CREATE ASSEMBLY FROM BINARY 会破坏托管实例和 SQL Server 2008 R2 上的EXTERNAL NAME 子句的语法。如果有人对此有答案,我很想听听!在我进行的所有搜索中,我还没有找到任何人发布了一个实际有效的CREATE ASSEMBLY FROM BINARY 示例。 罗恩,不,TRUSTWORTHY ON不是答案...._ever_....您是否将这些数据库恢复到托管实例?你为什么要重新加载 SQLCLR 的东西?...如果是因为安全错误,那很容易在不重新加载的情况下修复..但仍然需要准确了解您在做什么。 我使用 Azure SQL 迁移服务将 DB 加载到 MI。因此,这本身并不是真正的传统修复。我正在重新加载 SQLCLR,因为我的想法已经用完了。如果您知道在没有TRUSTWORTHY ON 的情况下解决安全问题的另一种方法,我很乐意听到。 我现在正在发布一个答案......但它并不短:(主要是因为修复这个问题的各种尝试导致了这里的多个问题...... 【参考方案1】:

所以,这里有一系列问题:

    第一个问题是未签名的程序集(或者甚至没有匹配的、基于签名的登录但已被授予UNSAFE ASSEMBLY 权限的已签名程序集)不再被视为“安全”,因为可能(但可能未经证实)安全问题。因此,从 SQL Server 2017 开始,引入了名为“CLR 严格安全”的新服务器级配置设置,并强制 所有 程序集满足标记为 UNSAFE 的程序集的要求。 SAFEEXTERNAL_ACCESSPERMISSION_SET 选项仍然像以前一样工作,这只是加载程序集并从中执行 SQLCLR 对象的问题。您遇到的问题是 SAFE 程序集永远不需要签名(尽管它们本来可以是,例如我的 SQL# SQLCLR 库中的所有程序集,并且对它们进行签名有好处,即使它们只包含SAFE 代码并将保留为 PERMISSION_SET = SAFE),因此大多数都没有签名,但现在他们需要签名。因此,在将数据库从 2017 年之前版本升级/迁移到 SQL Server 2017 或更高版本时,会引发安全错误。

一种选择是简单地禁用“CLR 严格安全”,或者另一种选择是为程序集所在的数据库启用TRUSTWORTHY。两者都是不太安全的选择。我不确定禁用“CLR 严格安全”是多么“不安全”(实际上,这可能是微软的一个巨大的过度反应),但显然首选是保持启用它。启用TRUSTWORTHY 绝对是一个非常糟糕的选择:PLEASE, Please, please Stop Using Impersonation, TRUSTWORTHY, and Cross-DB Ownership Chaining。

Microsoft 为解决这种情况而发布的机制是“受信任的程序集”功能。不幸的是,该功能也是一个糟糕的选择:它几乎从来不需要(嗯,只需要 Azure SQL 数据库,它不再支持 SQLCLR,但“受信任的程序集”代码已经编写好了)并且只是因为没有人知道现有的功能已经解决了这种情况,并且以更好的方式。 Please see: SQLCLR vs. SQL Server 2017, Part 4: “Trusted Assemblies” - The Disappointment。该帖子详细介绍了“受信任的程序集”的问题以及您应该如何应该解决现有的未签名程序集的问题(即在数据库中使用程序集创建证书、签署程序集、创建master 中的相同证书仅来自公钥,从 master 中的证书创建登录名,最后授予该登录名UNSAFE ASSEMBLY 权限。

    我尝试使用 ...sys.sp_add_trusted_assembly 解决程序集信任问题,它成功了,但没有任何改变。

我不确定您对散列使用了什么值,但该存储过程不会验证散列是否匹配任何内容。它只是将哈希加载到内部表中,以便稍后在引用程序集时从中读取。然后它将查看程序集的散列值是否与存储散列的内部表中的值匹配。所以它几乎总是会成功(只要你给它一个可以是 SHA2_256 哈希的有效二进制值)。

    我认为因为它暗示它无法从文件加载,这似乎是有道理的,因为我无法访问文件系统的 SQL MI 中不存在该文件

这只是一种误解。您是正确的,在 Azure SQL 数据库托管实例中没有文件系统访问权限,但在这种情况下,文件系统是存储通过 CREATE ASSEMBLY 加载的程序集的内部表。错误是说程序集无法从表中加载到内存中。这是由于上面第 1 项中提到的安全问题。所以删除和重新创建程序集实际上并没有做任何事情。

    Microsoft 论坛上的某个人建议我确保使用 Any CPU 选项编译 DLL

我不知道为什么会出现这种情况。同样,这里的任何代码都没有错。

    AS EXTERNAL NAME RegexFunction.[RegexSQLCLR.RegularExpressionFunctions].RegExIndex

不知道为什么RegexSQLCLR 被放在那里,但格式是(无命名空间):

AssemblyName.ClassName.MethodName

或(带有命名空间):

AssemblyName.[NameSpaceName.ClassName].MethodName

    同样,TRUSTWORTHY ON 是 bad choice, and entirely unnecessary。

【讨论】:

感谢所罗门的所有帮助。 “我不确定您对哈希使用了什么值,”此代码:DECLARE @hash_bytes varbinary(64); SELECT @hash_bytes=HASHBYTES('SHA2_512', 0x4D5A90000truncated for brevity) EXEC sys.sp_add_trusted_assembly @hash=@hash_bytes, @description=N'regexsqlclr, version=0.0.0.0, culture=neutral, publickeytoken=null, processorarchitecture=msil' 根据您引用的链接和其他地方的文档,哈希需要 SHA2_512。这不正确吗? @CB_Ron 嗯...确实 似乎 是正确的,假设 0x4D5A... 部分是 100% 完整且完全相同的二进制字符串,用于CREATE ASSEMBLY 声明。我从来没有使用过@description 参数,因为它是可选的,所以也许可以不使用它再试一次?另外,您是否有机会测试安装SQL# 的免费版本?只是好奇它是否有任何安装问题。上次我检查了获得测试帐户并不容易,但看起来他们在 12 月中旬增加了开发/测试定价,所以我也会调查一下。

以上是关于TSQL CREATE ASSEMBLY FROM varbinary 破坏了 class_name 参数的主要内容,如果未能解决你的问题,请参考以下文章

sql tsql__row-number_create_identity.sql

表连接Tsql基本编程和存储过程

TSQL 搜索文本

TSQL编程

Could not load type 'System.ServiceModel.Activation.HttpModule' from assembly 'System.Se

连接使用表值函数创建的表时优化 TSQL