从存储过程中调用sp_start_job


8

我们的开发人员需要能够从其.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

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

The EXECUTE permission was denied on the object 'sp_start_job', database 'msdb', schema 'dbo'.

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


1
解决了。该解决方案包括三部分:必须在服务器上启用所有权链接;EXECUTE AS语句中使用的用户必须是sa或具有类似权限的用户才能运行xp_sqlagent_ *作业;并且该作业必须由EXECUTE AS语句中列出的同一用户拥有。
Ed Leighton-Dick'2009年

更多的实验表明此解决方案有一个变体。如果要使用非SA代理用户来运行作业,则可以授予代理用户对master数据库中xp_sqlagent_ *过程的EXECUTE权限。(其他两个要求-跨数据库所有权和工作所有权-仍然适用。)
Ed Leighton-Dick

Answers:


5

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

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


是的-我首先将其添加到SQLAgentOperator角色,然后尝试对sp_start_job本身直接执行EXECUTE权限。都没有帮助。无论代理的权限如何,似乎都会引发此错误-即使是sysadmin级帐户也会失败。
Ed Leighton-Dick

使用SQL事件探查器,查看实际使用哪个帐户进行呼叫。现在,我考虑得更多了,Execute As就是使用一个数据库用户,该用户可能无法正确转换到其他数据库。尝试打开数据库链接,看看是否可行。
mrdenny

所有权链接是解决方案的重要组成部分,因此我在这里奖励了要点。事实证明,这还有另外两个方面。我会在上面指出这些。
Ed Leighton-Dick

8

很高兴您解决了此问题,但是不建议使用所有权链接。由于您似乎确实担心所涉及权利的安全性和正确性,因此我添加了此回复(尽管很晚),以作为正在发生的事情以及如何解决此问题的参考。

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因为in中msdb。还有许多其他示例,例如尝试访问服务器范围DMV,尝试使用链接的服务器或尝试将Service Broker消息传递到另一个数据库中。

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

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

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

通过跨数据库所有权链接解决此问题时,您已在整个服务器级别启用了跨数据库链接,这被认为存在安全风险。要获得所需结果的最受控,最细粒度的方法是使用代码签名:

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

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

  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_job中创建的证书派生用户的执行权限msdb

这是对应的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

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

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


0

由于您尝试从.NET代码启动SQL Server代理,因此这对于StackOverflow可能是一个更好的问题?

http://www.stackoverflow.com


1
我认为这可能是数据库安全问题,但是如果我们在这里找不到答案,我将尝试StackOverflow。
Ed Leighton-Dick

0

在网络上检查随机SQL实例SQLAgentOperatorRole不会直接为您提供sp_start_job特权,它会从SQLAgentUserRole继承它们。

使用以下命令仔细检查:

select dp.NAME AS principal_name,
                 dp.type_desc AS principal_type_desc,
                 o.NAME AS object_name,
                 p.permission_name,
                 p.state_desc AS permission_state_desc 
    from    sys.database_permissions p
    left    OUTER JOIN sys.all_objects o on p.major_id = o.OBJECT_ID
    inner   JOIN sys.database_principals dp on p.grantee_principal_id = dp.principal_id
    where o.name = 'sp_start_job'

在MSDB中运行此程序,然后仔细检查您是否没有继承任何显式的拒绝访问。

hth。


该用户明确是SQLAgentOperatorRole和SQLAgentUserRole的成员。
Ed Leighton-Dick

0

在不授予其他权限的情况下实现此目标的一种方法:不要让存储的proc直接启动作业,而只允许存储的proc翻转表(在应用程序数据库中)中的位;然后,让作业每分钟左右运行一次,检查钻头是否翻转,如果是,则执行工作,然后再次翻转钻头。如果作业看到该位没有翻转,则作业将退出。

如果您不介意延迟(并且作业经常运行),那么它就像是一种魅力。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.