代码之家  ›  专栏  ›  技术社区  ›  GSerg

SQL Server 2008:加载非托管库的CLR存储过程的崩溃安全性如何

  •  4
  • GSerg  · 技术社区  · 16 年前

    我们在SQL Server中有一个常规(即非扩展)存储过程 二千 调用外部exe。反过来,该exe加载来自SDK的.dll并从中调用一些过程(即in it、dostuf、shutdown)。

    唯一的原因是我们不想创建一个调用.dll的扩展存储过程。我们相信,如果DLL崩溃(虽然不太可能发生,但仍然如此),那么SQL Server进程也会崩溃,这不是我们想要的。使用外部exe时,只有该exe会崩溃。

    现在,我们要升级到SQL Server 二千零八 并考虑创建一个clr存储过程来调用该对象,从而除去exe。当然,这个SP会被标记为不安全。因此,问题是,与扩展的SP方法相比,这样做是否安全(更安全、足够安全等)?

    我在BOL上找到的唯一相关的东西是:

    指定不安全允许 要执行的程序集非法 对SQL Server的操作 处理空间,因此可以 可能会影响鲁棒性 以及SQL Server的可扩展性

    但我不确定它是否回答了我的问题,因为我不追求“健壮性和可伸缩性”,而是追求稳定和保持运行。

    PS:我们想去掉这个exe,因为它在管理sp权限时会导致不公平(你知道,如果你调用一个包含xp-cmdshell的sp,它会突然应用到你身上)。

    2 回复  |  直到 8 年前
        1
  •  3
  •   Jason Kresowaty    16 年前

    由于此代码最初用于扩展存储过程,因此听起来像是非托管代码。非托管代码中的错误很容易使进程崩溃。

    clr集成比扩展存储过程更加健壮,但代码仍在进程内运行,因此错误可能会导致SQL Server停机或损坏。(相比之下,理论上,安全的clr例程将无法损坏SQL Server,尽管它可能会导致问题,从而在不完全关闭SQL Server的情况下降低服务器的可用性。)

    基本上,在这种情况下,避免SQL Server崩溃的唯一方法是:

    1. 避免使用崩溃的功能。
    2. 修正错误代码。
    3. 在单独的进程中运行代码(启动可执行文件、调用Windows服务、调用Web服务等)。您可以编写托管.NET DLL来执行此交互。最有可能的是,您仍然需要不安全地加载它,但是——如果它写得正确——实际上它是非常安全的。
        2
  •  1
  •   Solomon Rutzky    8 年前

    因此,问题是,与扩展的SP方法相比,这样做是否安全(更安全、足够安全等)?

    一般来说是的。我的意思是,如果你正在炮击一个操作系统进程,那么你正在炮击一个操作系统进程。我不知道如何使用扩展存储过程API来实现这一点必然比使用sqlcrl API更安全,尤其是当可能崩溃的事情是一个操作系统进程(位于数据库外部)时。

    当然,我不确定xp api,因为我没有使用它,但我知道以下几点:

    • 不推荐使用xp api,建议在任何一种技术中都可以完成的所有新项目都应该在sqlcr中完成。
    • sqlcr允许比其他两个更精细的权限,包括进行模拟的能力(如果执行sqlcr对象的登录名是Windows登录名)。
    • sqlcrl api由数据库和程序集所有者(即由 AUTHORIZATION 条款)。因此,一个数据库中的程序集可能会出现问题,而不会影响其他数据库中的sqlcr对象(或者即使在同一个数据库中,如果存在其他用户拥有的程序集,也会影响其他用户拥有的程序集),但实际上很少会出现这种情况,因为大多数人只使用默认值,即 dbo )

    我不确定它是否回答了我的问题,因为我不是在追求“健壮性和可伸缩性”,而是在追求稳定性和保持它的正常运行之后。

    当然,当程序集设置为 UNSAFE :

    • 可能写入注册表(取决于授予作为运行SQL Server进程的登录帐户的访问权限,或者如果启用了模拟并且是Windows登录,则取决于执行sqlcr函数的登录名)。
    • 可能写入文件系统
    • 可能与系统上运行的进程交互
    • 与从同一程序集中执行函数的其他SQL Server SPID(即会话)共享内存(这意味着该数据库中由该用户拥有的特定程序集)。这可能是最让人难以捉摸的,因为当你习惯于控制台应用程序和Windows应用程序有自己的内存空间时,这是出乎意料的,然而在这里,因为它是每个所有者每个数据库的每个程序集的一个AppDomain,所有执行该代码的会话都共享所有 static 变量。许多代码都是在假定AppDomain是私有的情况下编写的,因此在静态变量中存储值在缓存值时是有效的,但是在SQLCLR中,如果两个进程重写彼此的值并读取另一个会话的值,则可能会出现意外的行为。
    • 潜在内存泄漏。主机保护属性试图阻止您使用可执行此操作的内置.NET功能,例如使用 TimeZoneInfo 在TimeZoneID之间转换时间,但主机保护属性不在 不安全的 组件。
    • 它是 可能的 运行sqlcr方法的线程在执行不安全/完全信任代码(合作多任务vs抢占)时的处理方式不同。我以为我已经读到了不安全线程是以不同的方式管理的,但我不确定我在哪里读它并在寻找源代码。

    但是上面所说的,如果您调用的是外部exe,它有自己的appdomain。

    所以,你可以做的是:

    1. 继续使用sqlcr包装调用exe Process.Start() . 这就给了你们两个进程/内存分离 能够更轻松地控制对只调用此exe的单个存储过程的权限,任何人都无法更改它(至少在不更改该sqlcr代码并重新安装程序集的情况下不会更改)。

    2. 在同一台计算机上安装一个SQL Server Express实例,在该计算机上加载SQLCLR对象,并在两个方向(从当前SQL Server实例到新的SQL Server Express实例)创建链接服务器,以便在它们之间轻松通信。这将允许您隔离SQLCLR执行,并使其远离主SQL Server进程。

    当然, 那个 大家都说,这到底有多重要?这意味着,一个进程完全崩溃并用它来删除所有内容的可能性有多大?当然,这并非不可能,但通常崩溃只会破坏AppDomain,而不会破坏CLR主机本身。我认为人们遇到的问题更可能是代码没有崩溃,但是写得不好,占用了太多内存和/或CPU。