一个以PERMISSION_SET = SAFE方式创建的CLR程序集,如何能够访问外部系统资源、调用非托管代码和获取sysadmin权限?这是因为.NET Framework在4.5版本(我想)开始进行了安全性方面的改变。
Code Access Security Basics的MSDN文档说道:
.NET Framework提供了一种机制,可以对同一应用程序中的不同代码强制执行不同级别的信任,称为代码访问安全性(CAS)。在.NET Framework中,不应将代码访问安全性用作基于代码来源或其他身份方面的强制实施安全边界的机制。我们正在更新我们的指导方针,以反映出针对未知源代码的部分可信代码,代码访问安全性和安全透明代码将不被支持作为安全边界。我们建议在没有采取备选安全措施的情况下,不要加载和执行未知来源的代码。
然后,文档指向
.NET Framework中的安全性更改页面。
".NET Framework 4.5中最重要的安全变化在于强名称。文档
Enhanced Strong Naming指出:强名称密钥由签名密钥和标识密钥组成。程序集使用签名密钥进行签名,并由标识密钥标识。在.NET Framework 4.5之前,这两个密钥是相同的。从.NET Framework 4.5开始,标识密钥与早期.NET Framework版本相同,但签名密钥使用更强的哈希算法进行增强。此外,签名密钥使用标识密钥进行签名以创建反向签名。另外,文档
Secure Coding Guidelines指出:不支持代码访问安全性和安全透明代码作为部分受信任代码的安全边界。我们建议不要加载和执行未知来源的代码,除非实施了替代安全措施..."
因此,.NET 的安全模型已经改变多年,但 SQL Server(直到 SQL Server 2017)一直被允许继续使用旧的安全模型。似乎从 SQL Server 2017 开始,决定不再支持旧的安全模型。
我怀疑允许使用旧的安全模型是为了:
- 防止 SQL Server(至少是 CLR 相关的功能/组件)基于更新的 .NET Framework 版本,并且
- 导致 SQLCLR 作为 Azure SQL Database 支持的功能突然被移除(支持在 2014 年底推出 v12 时已添加,但在 2016 年 4 月 15 日完全删除)。
所以,是的,这有点糟糕。至少现在意味着需要首先创建一个证书或非对称密钥(已用于签名任何要加载的程序集)到[master]
,然后创建一个登录用户,并授予该登录用户UNSAFE ASSEMBLY
权限。这与加载EXTERNAL_ACCESS
和UNSAFE
程序集所需的事件序列相同,但现在,不幸的是,即使是SAFE
程序集也需要这样做。
目前没有机制可以以完全可移植的方式处理此问题(即不依赖于外部文件),也无法通过Visual Studio / SSDT进行处理而不需要手动干预。这种情况已经存在了一段时间,但至少可以创建一个设置来以完全可移植的方式处理此问题(即完全包含在.sql脚本中):请参见Stairway to SQLCLR Level 7: Development and Security获取详细信息(这是我撰写的一篇文章)。
可以从十六进制字节(即
FROM BINARY = 0x...
)创建证书,但在使用证书时需要使用
signtool
,而MSBuild使用
sn
。这在Visual Studio(依赖于MSBuild)/ SSDT中无法实现。
为了使Visual Studio / MSBuild / SSDT发布过程可行(这反过来意味着任何人都能够创建完全自包含的.sql脚本,能够创建非对称密钥而不依赖外部文件),
CREATE ASYMMETRIC KEY
命令需要增强以允许从二进制字符串创建。我已经在Microsoft Connect上提出了这个建议-
允许像CREATE CERTIFICATE一样从二进制十六进制字节字符串创建非对称密钥-请支持它:-)。
或者(暂时),您可以尝试我在以下博客文章中描述的两种技术之一(两者都与SSDT完全兼容)。
作为最后的手段,您可以考虑以下方法:
TEMPORARILY set the [master]
Database to TRUSTWORTHY ON
For the next step (i.e. CREATE ASSEMBLY
) to execute successfully, the Login that is the database owner (i.e. same SID used by the [dbo]
User of [master]
) needs to have the UNSAFE ASSEMBLY
permission. If [master]
is owned by sa
or any other sysadmin, then it has all permissions and this requirement has been satisfied. But, if [master]
is owned by a low-privileged login (a "best practice"), then you will need to execute the following statement in order for the CREATE ASSEMBLY
to work when TRUSTWORTHY
is ON
:
EXEC (N'USE [master]; GRANT UNSAFE ASSEMBLY TO [{DB_Owner_Login}];');
- Create the Assembly in
[master]
- Create the Asymmetric Key from the Assembly
- Drop the Assembly
- set the
[master]
Database to TRUSTWORTHY OFF
- Create the Login from the Asymmetric Key
- Grant
UNSAFE ASSEMBLY
to that Login (this replaces the need for the DB where the Assembly is loaded to be set to TRUSTWORTHY ON
and for its owner Login to have the UNSAFE ASSEMBLY
permission).
请注意,我在这里没有将新的“可信程序集”功能作为选项包含。原因是它有比好处更多的缺陷,更不用说它在第一次使用时完全是不必要的,因为现有功能已经处理了“可信程序集”所要解决的情况。有关详细信息和演示如何处理现有的未签名程序集,请参见:
SQLCLR vs. SQL Server 2017, Part 4: “Trusted Assemblies” – The Disappointment。
ALTER DATABASE YourDatabase SET TRUSTWORTHY ON
。 - Jesús López