在企业环境中,数据库的安全性至关重要。经常面临需要将数据库暴露给外部用户的情况,这可能会带来安全风险。外部用户可能是数据的下游消费者,或者是需要访问数据库以支持其应用程序的团队。如果这些用户试图非法访问他们不应该访问的数据,或者更糟糕的是,获得写入权限,甚至破坏或损坏数据,该怎么办?
本文将介绍一种方法,通过创建Facade数据库来为特定用户提供对数据库中特定表的受限访问,而无需直接授予对任何底层数据库/表的访问权限。SQL Server提供了一个名为跨数据库所有权链的功能,可以帮助实现这一点。本文中提供的示例已在SQL Server 2008 R2服务器上开发和测试。此功能也支持较旧版本的SQL Server,但讨论将限于以下版本:
当脚本顺序访问多个数据库对象时,这种顺序被称为链。尽管这样的链并不独立存在,但当SQL Server遍历链中的链接时,它对组成对象的权限评估与单独访问对象时不同。这些差异对于管理访问和安全性具有重要意义。
当通过链访问对象时,SQL Server首先比较对象的所有者与调用对象的所有者。如果两个对象具有相同的所有者,则不会评估引用对象的权限。
SQL Server可以配置为允许特定数据库之间或SQL Server单个服务器内所有数据库之间的所有权链。跨数据库所有权链默认情况下是禁用的,除非特别需要,否则不应启用。要使跨数据库所有权链工作,涉及的数据库必须具有共同的所有者。
跨数据库链可以在服务器级或个别数据库级别启用。在服务器级启用它会使跨数据库所有权链在服务器上的所有数据库中工作,而不管数据库的个别设置如何。如果只需要为少数数据库启用它,那么应该在数据库级别启用它。
要启用服务器级跨数据库所有权链,请使用以下T-SQL语句。
EXECUTE sp_configure 'show advanced', 1; RECONFIGURE; EXECUTE sp_configure 'cross db ownership chaining', 1; RECONFIGURE;
要检查它是否已经启用,请使用此查询:
SELECT [name], value FROM [sys].configurations WHERE [name] = 'cross db ownership chaining';
值为1表示它已经启用。
要启用数据库级跨数据库所有权链,请使用以下T-SQL语句。
ALTER DATABASE myDatabase SET DB_CHAINING ON;
要检查它是否已经在个别数据库级别启用,请运行:
SELECT name, is_db_chaining_on FROM sys.databases;
让通过一个例子来说明。创建一个名为CustomerDB的数据库。然后,创建一个名为Customers的表并插入一些测试数据。
CREATE TABLE [dbo].[Customers]( [CustomerId] [int] IDENTITY(1,1) NOT NULL, [CustomerName] [varchar](50) NOT NULL, [Address] [varchar](500) NOT NULL, [City] [varchar](50) NOT NULL, [Country] [varchar](50) NOT NULL, CONSTRAINT [PK_Customers] PRIMARY KEY CLUSTERED ([CustomerId] ASC) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] ) ON [PRIMARY];
插入测试数据:
INSERT INTO Customers ([CustomerId], [CustomerName], [Address], [City], [Country]) VALUES (1, 'Michael Douglas', 'LA Home', 'Los Angeles', 'US'); INSERT INTO Customers ([CustomerId], [CustomerName], [Address], [City], [Country]) VALUES (2, 'Al Pacino', 'NY Home', 'New York', 'US'); INSERT INTO Customers ([CustomerId], [CustomerName], [Address], [City], [Country]) VALUES (3, 'James Cameron', 'NJ Home', 'New Jersey', 'US');
创建一个名为FacadeDB(或任何其他名称)的数据库。
为主数据库中希望授予受限用户访问权限的每个表创建视图。
CREATE VIEW [dbo].[CustomerView] AS SELECT * FROM CustomerDB.dbo.Customers;
对象资源管理器现在应该看起来像这样:
为主数据库创建受限用户登录及其关联的用户。用户必须作为“public”添加到主数据库中。否则,所有权链将无法工作。用户至少必须在Facade数据库中具有“db_datareader”角色。
CREATE LOGIN [FacadeUser] WITH PASSWORD=N'facadeuser', DEFAULT_DATABASE=[FacadeDB], DEFAULT_LANGUAGE=[us_english], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF; USE [CustomerDB]; CREATE USER [FacadeUser] FOR LOGIN [FacadeUser] WITH DEFAULT_SCHEMA=[dbo]; USE [FacadeDB]; CREATE USER [FacadeUser] FOR LOGIN [FacadeUser] WITH DEFAULT_SCHEMA=[dbo]; EXEC sp_addrolemember N'db_datareader', N'FacadeUser';
ALTER DATABASE CustomerDB SET DB_CHAINING ON; ALTER DATABASE FacadeDB SET DB_CHAINING ON;
以受限用户(FacadeUser)身份登录到服务器,并执行以下命令。
SELECT * FROM CustomerView;
应该能够看到底层表的行。现在,尝试直接查询底层表。
SELECT * FROM CustomerDB.dbo.Customers;
应该看到此错误:
在对象'Customers',数据库'CustomerDB',架构'dbo'上拒绝SELECT权限。