我有一个带有两个数据库的SQL Server:
登录“ 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查询
答案 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;