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 将程序集编写为 DROP
和 CREATE
,它使用 FROM BINARY
语法,并同样编写了所有函数的脚本。 CREATE ASSEMBLY
成功了,所以我认为我在正确的轨道上。然后我尝试创建第一个函数和BAM,另一个错误!这次错误读取
消息 6505,级别 16,状态 2,过程 RegexIndex,第 2 行 在程序集“RegexFunctions”中找不到类型“RegexSQLCLR.RegularExpressionFunctions”。
我已经在谷歌上搜索了几个小时,试图找到解决该问题的方法,但运气却为零。尽管 EXTERNAL NAME
子句的类部分的语法对于从文件加载的程序集非常有效。我验证了由 SSMS 编写的 varbinary
与原始 DLL 的二进制文件相同。 Microsoft 论坛上的某个人建议我确保使用 Any CPU
选项编译 DLL - 确实如此。作为健全性检查,我在其中一台本地服务器(即DROP
和CREATE ASSEMBLY FROM BINARY
)上执行了相同的过程,得到了完全相同的结果:我无法加载任何 CLR 函数!我已经尝试了我能想到的所有可能的类名排列,但无济于事。这是CREATE ASSEMBLY
和CREATE 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_files
的name
列中指定的名称;否则,原始 DLL 的完整路径位于 name
列中。
【问题讨论】:
当我们试图解决这个问题时,您为什么不尝试安装我创建的 SQLCLR 库SQL#,因为它包含相当多的 RegEx 函数和许多其他内容。您不需要所有程序集,因此您可以在标题注释下方的部分中将一些设置为“0”(例如Network
、OS
、Twitterizer
)和@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
的程序集的要求。 SAFE
和 EXTERNAL_ACCESS
的 PERMISSION_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
Could not load type 'System.ServiceModel.Activation.HttpModule' from assembly 'System.Se