从存储过程调用 sp_start_job

Ed *_*ick 9 sql sql-server sql-server-2005

我们的开发人员需要能够从他们的 .Net 代码启动 SQL Server 代理作业。我知道我可以调用msdb..sp_start_job来做到这一点,但我不想让一般用户帐户直接访问运行作业。

我想要做的是使用 WITH EXECUTE AS 子句在应用程序的数据库中创建一个存储过程来模拟代理帐户。我们的程序是:

CREATE PROCEDURE dbo.StartAgentJob 
    WITH EXECUTE AS 'agentProxy'
AS
BEGIN
    EXEC msdb.dbo.sp_start_job N'RunThisJob';
END
Run Code Online (Sandbox Code Playgroud)

但是,当我们运行它时,我们会收到以下消息:

The EXECUTE permission was denied on the object 'sp_start_job', database 'msdb', schema 'dbo'.
Run Code Online (Sandbox Code Playgroud)

有任何想法吗?这甚至是在 SQL2005 中执行此操作的最佳方法吗?

Rem*_*anu 9

我很高兴你解决了这个问题,但所有权链接不是推荐的解决方案。由于您似乎非常关心所涉及权利的安全性和适当的粒度,因此我添加了此回复,尽管很晚,但作为对正在发生的事情以及如何解决此问题的参考。

EXECUTE AS 模拟范围

EXECUTE AS 子句有两种形式:EXECUTE AS LOGIN 和 EXECUTE AS USER。EXECUTE AS LOGIN 由服务器验证,是整个 SQL 实例(服务器范围)信任的模拟上下文:

使用 EXECUTE AS LOGIN 语句模拟主体时,或使用 EXECUTE AS 子句在服务器范围的模块中模拟主体时,模拟的范围是服务器范围的。这意味着在上下文切换之后,可以访问模拟登录名有权访问的服务器中的任何资源。

EXECUTE AS USER 由数据库进行身份验证,并且是仅由该数据库(数据库范围)信任的模拟上下文:

但是,当使用 EXECUTE AS USER 语句模拟主体时,或使用 EXECUTE AS 子句模拟数据库范围的模块时,默认情况下模拟范围仅限于数据库。这意味着对数据库范围之外的对象的引用将返回错误。

具有 EXECUTE AS 子句的存储过程将创建一个数据库范围的模拟上下文,因此将无法引用数据库外部的对象,例如您将无法引用,msdb.dbo.sp_start_job因为在msdb. 还有许多其他示例可用,例如尝试访问服务器范围的 DMV、尝试使用链接服务器或尝试将 Service Broker 消息传递到另一个数据库。

使数据库范围的模拟能够访问通常不允许的资源,模拟上下文的身份验证器必须是可信的。对于数据库范围的模拟,身份验证器是数据库 dbo。这可以通过两种可能的方式实现:

  • 通过在对模拟上下文进行身份验证的数据库(即发出 EXECUTE AS 子句的数据库)上打开 TRUSTWORTHY 属性。
  • 通过使用代码签名。

这些详细信息在 MSDN:使用 EXECUTE AS 扩展数据库模拟中进行了描述。

当您通过跨数据库所有权链接解决问题时,您已在整个服务器级别启用了跨数据库链接,这被视为存在安全风险。实现预期结果的最可控、最细粒度的方法是使用代码签名:

  • 在应用程序数据库中创建一个自签名证书
  • dbo.StartAgentJob用这个证书签署
  • 删除证书的私钥
  • 将证书导出到磁盘
  • 将证书导入 msdb
  • 从导入的证书创建派生用户 msdb
  • 向派生用户授予 AUTHENTICATE 权限 msdb

这些步骤确保过程的 EXECUTE AS 上下文dbo.StartAgentJob现在是可信的msdb,因为上下文由在 中具有 AUTHENTICATE 权限的主体签名msdb。这解决了一半的难题。另一半是实际授予msdb.dbo.sp_start_job现在受信任的模拟上下文的 EXECUTE 权限。有几种方法可以做到这一点:

  1. 映射模拟用户agentProxy用户msdb并授予他执行权限msdb.dbo.sp_start_job
  2. 授予msdb身份验证器证书派生用户执行权限
  3. 向过程添加一个新签名,为其派生一个用户,msdb并授予该派生用户执行权限

选项1.很简单,但有一个很大的缺点:agentProxy用户现在可以随意执行msdb.dbo.sp_start_job,他真正被授予访问权限msdb并拥有执行权限。

选项 3 是肯定正确的,但我觉得是不必要的矫枉过正。

所以我首选的是选项 2:授予msdb.dbo.sp_start_jobmsdb.

下面是对应的SQL:

use [<appdb>];
go

create certificate agentProxy 
    ENCRYPTION BY PASSWORD = 'pGFD4bb925DGvbd2439587y'
    with subject = 'agentProxy'
   , start_date='01/01/2009';
go

ADD SIGNATURE TO OBJECT::[StartAgentJob]
      BY CERTIFICATE [agentProxy]
        WITH PASSWORD = 'pGFD4bb925DGvbd2439587y';
go

alter certificate [agentProxy] 
  remove private key;
go

backup certificate [agentProxy] 
 to file='c:\temp\agentProxy.cer';
go

use msdb
go

create certificate [agentProxy] 
  from file='c:\temp\agentProxy.cer';
go

create user [agentProxyAuthenticator] 
 from certificate [agentProxy];
go

grant authenticate to [agentProxyAuthenticator];
grant execute on msdb.dbo.sp_start_job to [agentProxyAuthenticator];
go

use [<appdb>];
go

exec dbo.StartAgentJob;
go
Run Code Online (Sandbox Code Playgroud)

我的博客中有一些涉及这个主题的文章,是在 Service Broker 激活过程的上下文中编写的(因为它们需要 EXECUTE AS 子句):

顺便说一句,如果您要测试我的脚本并且您住在东半球或英国夏令时,请务必阅读我在测试前链接的最后一篇文章。


mrd*_*nny 5

您是否已将 agentProxy 登录名放入 msdb 数据库并授予其运行 sp_start_job 的权限?如果不是,您将需要为 msdb 数据库和您的用户数据库启用数据库权限链接。

您最好将登录名放入 msdb 数据库并授予它正确的权限。