将存储过程的权限授予另一个架构中的表

Bua*_*aXD 2 schema security sql-server stored-procedures permissions

我在 SQL Server 2012 数据库中创建了一个用户,并撤销了该角色授予的所有权限public

然后我授予了EXECUTE存储过程的权限。用户可以执行该过程,但无法获取其返回的数据。

该过程位于 中schema1,它从中选择的表位于 中schema2

如果我将用户添加到db_datareader角色,它可以从数据库中的所有表中读取所有数据。我尝试使用WITH EXECUTE AS OWNER但没有成功。

我怎样才能只授予对给定过程的访问权限而不授予其他任何权限?

Sol*_*zky 7

不,您不想要(或不需要)WITH EXECUTE AS OWNER

问题很简单,您的两个模式(Schema1存储过程和Schema2表)具有不同的所有者。如果执行以下查询,您将看到谁拥有哪些架构:

SELECT *, USER_NAME([principal_id]) AS [UserName]
FROM   sys.schemas;
Run Code Online (Sandbox Code Playgroud)

所有权链是默认的安全机制,如果所有者相同,则假定对正在访问的对象所引用的对象具有 DML 和 EXECUTE 权限。如果没有明确地将所有者分配给对象(默认 = NULL),则“所有者”被视为该对象所在架构的所有者。

因此,从技术上讲,您的模式可以具有相同的所有者,只是由于显式指定了表(通过ALTER AUTHORIZATION)而使表具有不同的所有者,但模式更有可能具有不同的所有者。

幸运的是,使用模块签名很容易解决这种情况。

其概念是在包含这些架构和对象的数据库中创建一个证书,然后使用该证书对存在于 中Schema1但从 中的表中进行选择的存储过程进行签名Schema2

用户是根据证书创建的,然后被分配所需的任何权限,以便存储过程成功完成。

不会向将执行该存储过程的任何人授予任何权限。权限仅授予基于证书的用户。存储过程和基于证书的用户之间的连接(实际上)是证书的公钥。并且仅当执行使用该证书签名的模块时,该公钥才会加载到会话的安全上下文中。该签名需要证书的密码(在ADD SIGNATURE声明中),并且如果任何人更改了存储过程(或其所有者)的单个字节,该签名就会被删除。因此,非常安全。

下图说明了这个概念:

主要设置

CREATE SCHEMA [Schema1];
GO
CREATE SCHEMA [Schema2];
GO

CREATE USER [TestUser] WITHOUT LOGIN;

CREATE TABLE [Schema2].[Stuff]
(
  [StuffID] INT NOT NULL IDENTITY(1, 1)
    CONSTRAINT [PK_Stuff] PRIMARY KEY,
  [Stuff] NVARCHAR(50) NOT NULL
);

INSERT INTO [Schema2].[Stuff] ([Stuff]) VALUES (N'Success!!');

GO
CREATE PROCEDURE [Schema1].[GetStuff]
AS
SET NOCOUNT ON;

SELECT [StuffID], [Stuff]
FROM   [Schema2].[Stuff];
GO

GRANT EXECUTE ON [Schema1].[GetStuff] TO [TestUser];
Run Code Online (Sandbox Code Playgroud)

测试相同的架构所有者

EXECUTE AS USER = N'TestUser';
SELECT SUSER_NAME() AS [Login], USER_NAME() AS [User];

EXEC [Schema1].[GetStuff];
-- Success due to both Schemas having same owner


REVERT;
SELECT SUSER_NAME() AS [Login], USER_NAME() AS [User];
Run Code Online (Sandbox Code Playgroud)

再现现状

SELECT *, USER_NAME([principal_id]) AS [UserName]
FROM   sys.schemas
WHERE  [name] IN (N'Schema1', N'Schema2');
/*
name       schema_id    principal_id    UserName
Schema1    5            1               dbo
Schema2    6            1               dbo
*/


ALTER AUTHORIZATION ON SCHEMA::[Schema2] TO [guest];


SELECT *, USER_NAME([principal_id]) AS [UserName]
FROM   sys.schemas
WHERE  [name] IN (N'Schema1', N'Schema2');
/*
name       schema_id    principal_id    UserName
Schema1    5            1               dbo
Schema2    6            2               guest
*/
Run Code Online (Sandbox Code Playgroud)

测试当前情况(无额外权限)

EXECUTE AS USER = N'TestUser';
SELECT SUSER_NAME() AS [Login], USER_NAME() AS [User];

EXEC [Schema1].[GetStuff];
-- ERROR:
/*
Msg 229, Level 14, State 5, Procedure Schema1.GetStuff, Line XXXXX [Batch Start Line YYYYY]
The SELECT permission was denied on the object 'Stuff', database '......', schema 'Schema2'.
*/


REVERT;
SELECT SUSER_NAME() AS [Login], USER_NAME() AS [User];
Run Code Online (Sandbox Code Playgroud)

模块签名设置

-- Create Certificate and sign Stored Procedure with it:
CREATE CERTIFICATE [Permission$Schema2]
    ENCRYPTION BY PASSWORD = 'SomePassword'
    WITH SUBJECT = 'Used for selecting from objects in Schema2',
    EXPIRY_DATE = '2099-12-31';


ADD SIGNATURE
    TO [Schema1].[GetStuff]
    BY CERTIFICATE [Permission$Schema2]
    WITH PASSWORD = 'SomePassword';


-- Create Certificate-based User to hold permissions
CREATE USER [Permission$Schema2]
  FROM CERTIFICATE [Permission$Schema2];


-- Grant Certificate-based User ONLY the permissions needed
GRANT SELECT ON SCHEMA::[Schema2] TO [Permission$Schema2];
Run Code Online (Sandbox Code Playgroud)

使用模块签名进行测试

EXECUTE AS USER = N'TestUser';
SELECT SUSER_NAME() AS [Login], USER_NAME() AS [User];

EXEC [Schema1].[GetStuff];
-- 1    Success!!!


REVERT;
SELECT SUSER_NAME() AS [Login], USER_NAME() AS [User];
Run Code Online (Sandbox Code Playgroud)