首页 > 解决方案 > SQL Server 所有权链接是否不适用于 ALTER?

问题描述

我有一个案例,SQL Server 所有权链接似乎不起作用 - 还是我遗漏了什么?

我有两个模式:Schema1Schema2.

Schema1中,我只有SELECT权限,而在 中Schema2,我只有EXEC权限。

我调用一个存储过程,在Schema2其中将记录插入到Schema1.

由于所有权链接,这可以正常工作(即使我没有INSERT权限)。Schema1

现在,当我调用另一个在插入之前关闭表中的标识列的存储过程时,我得到一个错误:

Msg 1088, Level 16, State 11, Procedure Schema2.AddRecordWithSpecificId, Line 7 [Batch Start Line 60]
Cannot find the object "Schema1.MyTable" because it does not exist or you do not have permissions.

如果我授予ALTER权限Schema1,它可以正常工作 - 但为什么有必要这样做?为什么在这种情况下模式链接不起作用?

重现问题的脚本:

CREATE DATABASE OwnershipChainingTest
GO

USE OwnershipChainingTest
GO

CREATE SCHEMA Schema1 AUTHORIZATION [dbo]
GO
CREATE SCHEMA Schema2 AUTHORIZATION [dbo]
GO

CREATE TABLE Schema1.MyTable
(
    Id int IDENTITY(1,1) NOT NULL,
    Title varchar(50) NOT NULL
)
GO

CREATE PROCEDURE Schema2.AddRecord
    @title nvarchar(100) 
AS
BEGIN
    INSERT INTO Schema1.MyTable (Title) 
    VALUES (@title)
END
GO

CREATE PROCEDURE Schema2.AddRecordWithSpecificId
    @id int,
    @title nvarchar(100) 
AS
BEGIN
    SET IDENTITY_INSERT Schema1.MyTable ON

    INSERT INTO Schema1.MyTable (Id, Title) 
    VALUES (@id, @title)

    SET IDENTITY_INSERT Schema1.MyTable OFF
END
GO

CREATE USER MyUser WITHOUT LOGIN
GO

CREATE ROLE MyRole AUTHORIZATION [dbo]
GO

EXEC sp_addrolemember MyRole, MyUser
GO

-- With this it works: GRANT SELECT, ALTER ON Schema::Schema1 TO MyRole
GRANT SELECT ON Schema::Schema1 TO MyRole
GO
GRANT EXEC ON Schema::Schema2 TO MyRole
GO

EXEC AS user = 'MyUser'

EXEC Schema2.AddRecord 'hello1'
GO

-- This causes an error
EXEC Schema2.AddRecordWithSpecificId 42, 'hello2'
GO

REVERT;
--SELECT CURRENT_USER

SELECT * FROM Schema1.MyTable

USE MASTER
DROP DATABASE OwnershipChainingTest
GO

标签: sql-serverdatabase-design

解决方案


所有权链接仅适用于 DML。SET IDENTITY_INSERT本质上是一个 DDL 操作,这就是为什么它至少需要ALTER对表的权限。

允许仅具有执行权限的最低权限用户运行 proc 的一种好方法是使用基于具有ALTER权限的用户的证书对 proc 进行签名:

--create certificate and sign proc
CREATE CERTIFICATE AddRecordWithSpecificIdCert
   ENCRYPTION BY PASSWORD = 'temporary password'
   WITH SUBJECT = 'Allow ALTER on Schema1';
ADD SIGNATURE TO  Schema2.AddRecordWithSpecificId BY CERTIFICATE AddRecordWithSpecificIdCert WITH PASSWORD = 'temporary password';

--remove ephemeral private key
ALTER CERTIFICATE AddRecordWithSpecificIdCert REMOVE PRIVATE KEY;

--create a user from certificate with the needed permissions
CREATE USER AddRecordWithSpecificIdCertUser FROM CERTIFICATE AddRecordWithSpecificIdCert;
GRANT ALTER ON SCHEMA::Schema1 TO AddRecordWithSpecificIdCertUser;
GO

--this test now works
EXEC AS user = 'MyUser';
GO
EXEC Schema2.AddRecordWithSpecificId 42, 'hello2'
GO
REVERT;
GO

推荐阅读