【问题标题】:System.Security.SecurityException with CLR Function带有 CLR 函数的 System.Security.SecurityException
【发布时间】:2019-10-31 15:11:13
【问题描述】:

首先,我想说这个问题与my previous post有关。

不过,我会将所有内容移到这里以供参考。

我遇到的问题是我仍然收到错误:

消息 6522,级别 16,状态 1,过程 PerfInsert,第 0 行 [批处理开始第 31 行] 执行用户定义的例程或聚合“PerfInsert”期间发生 .NET Framework 错误: System.Security.SecurityException:请求失败。 System.Security.SecurityException: 在 MiddleMan.MiddleMan.CreateCommand(SqlString tblString,SqlString featureName,SqlString connectionString,SqlString perfionConnectionString,SqlString logFile) .

尽管我相信我已经按照所有必要的步骤进行了正确设置。我什至已经验证了 SQL Server 是否具有文件目录的权限。

有谁知道我还能检查什么以查看丢失的部分是什么?

或者我是否需要将其设为“不安全”的程序集?

C#代码:

using Microsoft.SqlServer.Server;
using System;
using System.Data;
using System.Data.SqlClient;
using System.Data.SqlTypes;
using System.Diagnostics;

    namespace MiddleMan
{
    public static class MiddleMan
    {
        [SqlProcedure(Name = "PerfInsert")]
        public static SqlInt32 CreateCommand(SqlString tblString, SqlString featureName, SqlString connectionString, SqlString perfionConnectionString, SqlString logFile)
        {
            Process compiler = new Process();
            compiler.StartInfo.FileName = "C:\\SQL Server C# Functions\\PerfionLoader\\PerfionLoader\\bin\\Release\\PerfionLoader.exe";
            compiler.StartInfo.Arguments = tblString.Value + " " + featureName.Value + " " + connectionString.Value + " " + perfionConnectionString.Value + " " + logFile.Value;
            //compiler.StartInfo.UseShellExecute = false;
            //compiler.StartInfo.RedirectStandardOutput = true;
            compiler.Start();
            return SqlInt32.Zero;
        }
    }
}

SQL 代码:

CREATE ASSEMBLY PerfInsert
    AUTHORIZATION dbo
    FROM '\\bk-int-1\c$\SQL Server C# Functions\MiddleMan\MiddleMan\bin\Release\MiddleMan.dll'
    WITH PERMISSION_SET = SAFE
GO


CREATE ASYMMETRIC KEY [Brock.Retail_Brock.Retail_Brock]
  AUTHORIZATION [dbo]
  FROM EXECUTABLE FILE = '\\bk-int-1\c$\SQL Server C# Functions\MiddleMan\MiddleMan\bin\Release\MiddleMan.dll';

  CREATE LOGIN BrokcRetail
    FROM ASYMMETRIC KEY [Brock.Retail_Brock.Retail_Brock]


CREATE PROCEDURE PerfInsert
    (
        @tblString nvarchar(max)
        , @featureName nvarchar(max)
        , @connectionString nvarchar(max)
        , @perfionConnectionString nvarchar(max)
        , @logFiel nvarchar(max)
    )
    AS EXTERNAL NAME PerfInsert.[MiddleMan.MiddleMan].[CreateCommand]
GO

【问题讨论】:

  • 启动一个随机的外部进程肯定不安全。无论如何,这段代码有什么意义?您可以从 SQL Server 代理作业执行外部程序。如果您放松安全性,您可以使用xp_cmd,但同样,如果代理可以 放松安全性,为什么还要这样做?
  • 这看起来像是一个经典案例,通过运行或启动您需要的任何代码的客户端应用程序可以更好地解决问题。如果需要从数据库端随意触发的能力,可以设置一个Service Broker队列,等待请求。在 SQL Server 的上下文中启动任意进程是一场安全噩梦。
  • 您必须使用 EXTERNAL_ACCESS 或 UNSAFE 并且代码将在 SQL Server 服务帐户下运行。这不是一个好主意。无论你想做什么,你需要使用 SQLCLR 来做,更不用说允许不安全的代码执行
  • 好吧,你肯定需要一个UNSAFE 程序集——如果你使用的是 SQL Server 2017,你会遇到additional difficulties with the new default permissions(由同一类灵魂撰写回答了你的另一个问题,谁也可能会出现在这个问题上)。
  • @PanagiotisKanavos 我当然同意,不幸的是,Microsoft 让 SQLCLR 支持乏善可陈,这令人沮丧且远非理想。但是,我不同意“不完美 == 不可行”的观点。该功能存在并且在许多情况下都可以使用,因此我认为使用它没有整体问题。当然,这个特定的实现可能是不明智的,最好通过xp_cmdshell 或 SQL 代理作业来完成。但是,这通常与 SQLCLR 无关。你不喜欢或不使用它很好,但它并不像你描述的那么糟糕。

标签: c# sql-server sqlclr


【解决方案1】:

您正在使用多线程,所以是的,程序集 100% 需要有 PERMISSION_SET = UNSAFE

此外,由于您已经设置了非对称密钥和关联的登录(感谢您这样做,并且使用TRUSTWORTHY ON),您需要在设置程序集之前执行以下操作致UNSAFE

USE [master];
GRANT UNSAFE ASSEMBLY TO [BrokcRetail];

然后:

USE [{db_containing_assembly_hopefully_not_master];
ALTER ASSEMBLY [PerfInsert] WITH PERMISSION_SET = UNSAFE;

或者,如果您创建基于非对称密钥的登录并首先授予它UNSAFE ASSEMBLY 权限,那么您可以在CREATE ASSEMBLY 语句中简单地使用UNSAFE 而不是SAFE

从 SQL Server 2017 开始,您需要在创建程序集之前创建非对称密钥和关联的登录。非对称密钥和登录名进入[master],而程序集可以进入任何数据库(包括[master],但通常最好不要将自定义代码放入其中)。

如果您已经在使用 SQL Server 2017 或更高版本,并且如果问题中显示的代码按照您执行它的实际顺序,那么我猜您已经将数据库设置为 TRUSTWORTHY ON或禁用“CLR 严格安全”。否则,如果没有首先创建基于签名的登录授予UNSAFE ASSEMBLY 权限,您根本无法创建程序集。如果我对此正确,您可以重新启用“CLR 严格安全”和/或为该数据库转TRUSTWORTHY OFF

另外,正如我在您的相关问题中所指出的(与此问题相关的问题),您应该使用 SqlString 而不是 SqlCharsSqlString.Value 返回一个 .NET string,而 SqlChars.Value 返回一个 char[]。很久以前人们将SqlCharsNVARCHAR(MAX) 相关联,并将SqlStringNVARCHAR(1-4000) 相关联,但这只是由于Visual Studio / SSDT 在生成DDL 以发布数据库项目时使用这些映射作为默认值。但是它们之间从来没有任何技术/字符串映射。您可以将任一 .NET 类型与任一 T-SQL 数据类型一起使用。

此外,在 SQLCLR 中使用多线程时,请谨慎行事(并进行大量测试)。

请访问SQLCLR Info,了解更多与一般使用 SQLCLR 相关的资源。

相关帖子:

  1. System.Web in SQL Server CLR Function(在 DBA.StackExchange 上)
  2. CREATE PROCEDURE gets “Msg 6567, Level 16, State 2” for SQLCLR stored procedure

【讨论】:

  • 您不希望获得ReadOnlySpan<char> 而不是 SqlString 作为输入,这样您就可以处理它而不必担心临时字符串?
  • @SolomonRutsky 再次感谢您的帮助。我让一切正常,并决定按照您之前最初建议的方式实施它。如果我确实继续使用这种方法,我只是想至少正确组装所有移动部件。再次感谢您的帮助!
  • @DougCoats 欢迎您。很高兴我能帮助你。为了清楚起见,我最初建议的是哪种方式?通过 SQL 代理进行调度,还是只为此项目使用单独的 Express 实例?只是好奇。
  • @SolomonRutzky dba.stackexchange.com/questions/252077/… “我真的不明白为什么它不能是一个以这两种方式之一安排的控制台应用程序。看起来你可以简单地更改项目类型(成为控制台应用程序,编译为 .EXE 文件),并完成(在计划之外)。”基本上,我已经承认这不需要是一个 CLR 函数,只是更大工作的另一个步骤,但是,自从我打开蠕虫罐后,我觉得无论如何完成它都是一个很好的练习,即使我打算不同的方法..
  • @DougCoats 感谢您的更新。我将该信息添加到我对原始问题的回答中。而且,我完全同意测试各种选项以更好地理解它们,以便您做出最佳决策、最明智的决定。即使你没有在这里使用新知识,你也有这些信息和经验可以用于其他可以从这个练习中受益的项目。?
猜你喜欢
  • 1970-01-01
  • 2011-06-12
  • 2014-11-18
  • 2012-07-16
  • 1970-01-01
  • 2018-03-29
  • 1970-01-01
  • 1970-01-01
  • 2011-06-24
相关资源
最近更新 更多