跨数据库视图 - 无法在当前安全上下文下访问数据库

ROA*_*OAL 3 sql-server permissions sql-server-2012 users

我们有两个数据库,AdbBdb. 在Bdb,我们创建了一个观点,即引用另一种观点认为Adb,像这样:CREATE VIEW Bdb.dbo.Bview AS SELECT * FROM Adb.dbo.Aview

我们有一个 SQL Authenticated Blogin,映射到BuserAdbBdb,至少db_datareader在两者上都有角色。

以下不起作用:

USE Bdb;
EXECUTE AS USER = 'Buser';
SELECT * FROM Bdb.dbo.Bview;
SELECT * FROM Adb.dbo.Aview;
Run Code Online (Sandbox Code Playgroud)

两个选择都会引发以下错误:

Msg 916, Level 14, State 1, Line 4
The server principal "Buser" is not able to access the database "Adb" under the current security context.
Run Code Online (Sandbox Code Playgroud)

但是,这有效:

USE Adb;
EXECUTE AS USER = 'Buser';
SELECT * FROM Adb.dbo.Aview;
SELECT * FROM Bdb.dbo.Bview;
Run Code Online (Sandbox Code Playgroud)

我注意到当我第一次USE Bdb切换到 时Buser,我看不到任何其他数据库:

USE Bdb;
EXECUTE AS USER = 'Buser';
SELECT * FROM sys.databases; -- only master, tempdb and Bdb is shown
Run Code Online (Sandbox Code Playgroud)

但是,当我USE Adb第一次看到它们时,我看到了所有这些,即使是那些没有Buser并且无法通过它访问的:

USE Adb;
EXECUTE AS USER = 'Buser';
SELECT * FROM sys.databases; -- all DBs on the server are shown
Run Code Online (Sandbox Code Playgroud)

什么可能导致这个问题?我应该检查什么?

Sol*_*zky 6

第一件事:不要让信任成为可能!!绝对没有理由打开这么大的安全漏洞。(注意:msdbTRUSTWORTHY启用,这很好,因为它是 Microsoft 提供的数据库;用户创建的数据库永远不需要 TRUSTWORTHY启用)

现在,如果这在模拟用户而不是登录时有效,则是因为您的[Adb]数据库已被启用为TRUSTWORTHY ON,这将删除使用数据库级模拟时存在的默认隔离区。您可以通过执行以下操作来看到这一点:

SELECT db.is_trustworthy_on, *
FROM   sys.databases db
WHERE  db.[name] IN (N'Adb', N'Bdb');
Run Code Online (Sandbox Code Playgroud)

假设它是的情况下Adb为启用TRUSTWORTHYBdb没有,那么还是请不要启用TRUSTWORTHYBdb。这将是最好禁用 TRUSTWORTHYAdb,并使用模块签名来实现:

ALTER DATABASE [Adb] SET TRUSTWORTHY OFF;
Run Code Online (Sandbox Code Playgroud)

有关通过模块签名进行跨数据库访问的示例,请参阅我的以下答案(在 DBA.SE 上):

访问基于另一个数据库中的表的视图,而无需在该另一个数据库中的帐户

有关为什么应该使用模块签名而不是 TRUSTWORTHY(甚至跨数据库所有权链接)的更多信息,请参阅我的以下帖子:

请,请,请停止使用假冒、可信和跨数据库所有权链接

有关模块签名的更多信息,请参阅:

https://ModuleSigning.info/


正如@Nic 在对该问题的评论中提到的,最好在测试时使用EXECUTE AS LOGIN而不是使用EXECUTE AS USER。登录位于服务器级别,并且可以访问已为该登录创建用户的数据库。这就像以该帐户登录 SQL Server 一样。

使用 EXECUTE AS 扩展数据库模拟的 Microsoft 文档页面中说明了差异的原因

了解模拟范围

...

但是,当使用 EXECUTE AS USER 语句模拟主体时,或使用 EXECUTE AS 子句模拟数据库范围的模块时,默认情况下模拟范围仅限于数据库。这意味着对数据库范围之外的对象的引用将返回错误。

此外,“Extending Database Impersonation by Using EXECUTE AS”MSDN 页面(上面链接)上有很多很好的信息,解释了身份验证器和这些规则背后的推理。

鉴于这两个数据库是供应商提供的(补充的信息,我提交了这个答案后),那么它可能是最好的,只是开关EXECUTE AS LOGIN使该数据库的任何变化(模块签名)。