允许登录以运行存储过程,而无需从表中进行选择

时间:2019-01-17 11:32:13

标签: sql sql-server stored-procedures sql-server-2016

我有一个带有两个数据库的SQL Server:

  • 数据库1
  • 数据库2

登录“ MyLogin”仅对Database2具有读取权限。

Database2具有如下存储过程:

CREATE PROCEDURE Get_LogData 
       @ProductID int
AS
BEGIN

    If Exists (Select (1) From Database2.dbo.Products Where ProductID = @ProductID)
    Select LogTimeStamp
         , ProductID
         , Description
    from Database1.dbo.Log Where ProductID = @ProductID
    Else
    Print 'Get_LogData: Unable to find ProductID ' + CAST(@ProductID AS VARCHAR)

END;
GO

BEGIN
    GRANT EXEC ON Get_LogData TO [MyLogin];
END;
GO

当“ MyLogin”尝试运行存储过程时,我们收到错误消息:

  

服务器主体“ MyLogin”无法访问数据库   在当前安全上下文下为“ Database1”。

如何允许'MyLogin'运行此存储的proc并从Database1获取数据,而又不允许他们仅对Database1.Log运行常规的Select查询

2 个答案:

答案 0 :(得分:2)

这是Ezlo(SQL Server EXECUTE AS trouble)所链接的答案的扩展,我将在其上讨论Enabling Cross-Database Access in SQL Server

首先,让我们建立一个快速测试以复制您当前遇到的问题:

--Create a couple of sample databases
CREATE DATABASE SampleDB1;
CREATE DATABASE SampleDB2;
GO

USE SampleDB2;
GO
--Create a sample table
CREATE TABLE dbo.SampleTable (ID int, Somestring varchar(25));
GO
USE SampleDB1;
GO
--Create a sample SP and User/Login;
CREATE PROC dbo.SomeProc AS

    SELECT ID,
           SomeString
    FROM SampleDB2.dbo.SampleTable;
GO
CREATE LOGIN SampleCredential WITH PASSWORD = 'abc123', CHECK_EXPIRATION = OFF, CHECK_POLICY = OFF, DEFAULT_LANGUAGE = BRITISH;
CREATE USER SampleCredential FOR LOGIN SampleCredential;

GRANT EXEC ON dbo.SomeProc TO SampleCredential;
GO

--Test
EXECUTE AS LOGIN = 'SampleCredential';
GO
--This will fail
EXEC dbo.SomeProc;
GO
REVERT;
GO

如您所见,如果运行此脚本,则会出现以下错误:

  

信息916,级别14,状态1,过程SomeProc,第4行[批处理开始   第28行]服务器主体“ SampleCredential”无法访问   当前安全上下文下的数据库“ SampleDB2”。

那么,什么是跨数据库访问?引用文档:

  

跨数据库所有权链接发生在一个过程中的一个过程中   数据库取决于另一个数据库中的对象。跨数据库   所有权链的工作方式与   单个数据库,除非完整的所有权链要求   所有对象所有者都映射到相同的登录帐户。如果   源数据库中的源对象和目标数据库中的目标对象   目标数据库由同一登录帐户拥有,SQL Server执行   不检查目标对象的权限。

但是请注意,此方法可能会引入主要个安全漏洞。因此,如果这是您的环境问题,那么不是您的解决方案。再次,从文档中:

  

默认情况下,跨数据库的所有权链接处于关闭状态。   Microsoft建议您禁用跨数据库所有权   链接,因为它使您面临以下安全风险:

     
      
  • 数据库所有者和db_ddladmin或db_owners数据库角色的成员可以创建其他用户拥有的对象。这些   对象可以潜在地以其他数据库中的对象为目标。这表示   如果启用跨数据库所有权链接,则必须完全   使用所有数据库中的数据信任这些用户。

  •   
  • 具有CREATE DATABASE权限的用户可以创建新数据库并附加现有数据库。如果跨数据库所有权链接是   启用后,这些用户可以访问他们所访问的其他数据库中的对象   可能没有新创建或附加的特权   他们创建的数据库。

  •   

好的,现在告诫已经过去了,该怎么办。继续上面的脚本,我们需要在服务器上启用跨数据库所有权链接(如果尚未)。您可以通过运行以下命令来做到这一点:

EXEC sp_configure 'show advanced', 1;
RECONFIGURE;  
GO
EXEC sp_configure 'cross db ownership chaining', 1;  
RECONFIGURE;  
GO
EXEC sp_configure 'show advanced', 0;
RECONFIGURE;  
GO

现在已启用,您可以在 2 数据库上启用DB_CHAINING

ALTER DATABASE SampleDB1 SET DB_CHAINING ON;
ALTER DATABASE SampleDB2 SET DB_CHAINING ON;
GO

然后,您需要在另一个数据库上创建用于登录的用户,但是不需要授予该用户任何权限,因此这是“罚款”(不允许他们{{1} },从SP之外的表中获取)

SELECT

最后,您可以再次进行测试:

USE SampleDB2;
GO
CREATE USER SampleCredential FOR LOGIN SampleCredential;
GO

如果您要再次检查用户无法从数据库中进行选择,则可以这样做:

USE SampleDB1;
--Test
EXECUTE AS LOGIN = 'SampleCredential';
GO
--It works (0 rows returned)
EXEC dbo.SomeProc;
GO
REVERT;
GO

最后是清理(很好的措施):

USE SampelDB1;
GO
--This will fail
SELECT *
FROM SampleDB2.dbo.SampleTable;
GO

答案 1 :(得分:0)

您要使用execute as子句。尚不清楚该值应该是什么,但是要设置权限,通常您希望存储过程的 owner 具有所需的权限。

CREATE PROCEDURE Get_LogData (
       @ProductID int
)
WITH EXECUTE AS OWNER AS
BEGIN
    . . .
END;