在當今企業環境中,保證數據安全不是可有可無的工作。頻繁曝光的入侵和欺騙事件、薩班斯•奧克斯利法案、HIPAA法案規定和愛國者法案等都要求我們能夠做到,將正確數據提供給正確的用戶,防止其它無權限的人訪問。一般來說,“行級安全(row-level security)”的要求是:對數據庫中的數據以行為單位,設定只有特定用戶才可以訪問。可惜的是,SQL Server數據庫並不提供內置的行級別安全機制。
在本篇文章中,通過一個示例代碼(代碼清單1),來告訴大家一個在SQL Server中實現行級別安全的方法,以行為單位限定用戶的訪問權限,同時無需修改業務表的內容,不影響應用程序或表現層開發者,而且與用戶訪問數據的方式無關。該示例應用的模擬需求為:如何增加安全性到現有訂單數據庫中,限制經理只能訪問他們管理的部門或其子部門的數據,而不管用戶如何獲得該表,以及針對這個數據庫開發什麼樣的報表和查詢。
以下為引用的內容:
--create table script
CREATE TABLE dbo.UserAccess
(
UserID varchar(20) NOT NULL,
Department varchar(50) NOT NULL
)
CREATE TABLE [dbo].[Orders](
[OrderID] [int] NOT NULL,
[CustomerName] [varchar](20) NOT NULL,
[OrderTotal] [money] NOT NULL,
[Department] [varchar](50) NOT NULL
)
CREATE TABLE dbo.Departments
(
Department varchar(50) NOT NULL,
ParentDepartment varchar(50)
)
--end create table script
--script to clear then populate example tables
--clear tables
Delete from departments
Delete from orders
Delete from userAccess
--insert departments table
INSERT INTO [dbo].[Departments] ([Department],[ParentDepartment]) VALUES ('North America','')
INSERT INTO [dbo].[Departments] ([Department],[ParentDepartment]) VALUES ('East','North America')
INSERT INTO [dbo].[Departments] ([Department],[ParentDepartment]) VALUES ('Southeast','East')
INSERT INTO [dbo].[Departments] ([Department],[ParentDepartment]) VALUES ('Northeast','East')
INSERT INTO [dbo].[Departments] ([Department],[ParentDepartment]) VALUES ('West','North America')
INSERT INTO [dbo].[Departments] ([Department],[ParentDepartment]) VALUES ('Southwest','West')
INSERT INTO [dbo].[Departments] ([Department],[ParentDepartment]) VALUES ('Northwest','West')
--insert orders table
INSERT INTO [dbo].[Orders] ([OrderID],[CustomerName],[OrderTotal],[Department]) VALUES (1,'Harris','11.00','East')
INSERT INTO [dbo].[Orders] ([OrderID],[CustomerName],[OrderTotal],[Department]) VALUES (2,'Corrigan','22.00','Southeast')
INSERT INTO [dbo].[Orders] ([OrderID],[CustomerName],[OrderTotal],[Department]) VALUES (2,'Corrigan','22.00','Southeast')
INSERT INTO [dbo].[Orders] ([OrderID],[CustomerName],[OrderTotal],[Department]) VALUES (3,'Baldwin','33.00','Southeast')
INSERT INTO [dbo].[Orders] ([OrderID],[CustomerName],[OrderTotal],[Department]) VALUES (4,'Pillow','44.00','Northeast')
INSERT INTO [dbo].[Orders] ([OrderID],[CustomerName],[OrderTotal],[Department]) VALUES (5,'Carpenter','55.00','Northeast')
INSERT INTO [dbo].[Orders] ([OrderID],[CustomerName],[OrderTotal],[Department]) VALUES (6,'Meyer','66.00','West')
INSERT INTO [dbo].[Orders] ([OrderID],[CustomerName],[OrderTotal],[Department]) VALUES (7,'Gonzalez','77.00','Southwest')
INSERT INTO [dbo].[Orders] ([OrderID],[CustomerName],[OrderTotal],[Department]) VALUES (8,'Hall','88.00','Northwest')
INSERT INTO [dbo].[Orders] ([OrderID],[CustomerName],[OrderTotal],[Department]) VALUES (9,'Patrick','99.00','Southwest')
--insert user Access table
INSERT INTO [dbo].[UserAccess] ([UserID],[Department]) VALUES ('BLambert','Southwest')
INSERT INTO [dbo].[UserAccess] ([UserID],[Department]) VALUES ('MDavis','East')
INSERT INTO [dbo].[UserAccess] ([UserID],[Department]) VALUES ('MDavis','Southeast')
INSERT INTO [dbo].[UserAccess] ([UserID],[Department]) VALUES ('MDavis','Northeast')
INSERT INTO [dbo].[UserAccess] ([UserID],[Department]) VALUES ('WSimmons','Northeast')
--end script to clear then populate example tables
代碼清單1:提供了創建和加載示例表的腳本
主機網全新上線,買空間、服務器就上主機網,安全有保障!
行級別安全代碼范例
首先我們作出如下假設:
•使用SQL Server數據庫(微軟SQL Server 2000、2005或2008)
•所有表中都有一個共同的字段,使用它來決定誰可以看什麼數據(本例中就是department字段)
•通過不同的用戶id來加強應用程序調用數據庫的安全性,而不是通過一個單一的admin用戶id。
舉例來說,圖1所示數據結構包含:Orders表,包含一個客戶名稱、訂單收入和銷售部門;一個部門查閱表(Departments),包含父部門字段;一個用戶訪問表(UserAccess),在用戶和他有權限查看的部門之間建立聯系。用戶訪問表的每一行數據代表一個正確的用戶/部門組合。如果一個用戶有權限訪問的部門下設許多子部門,那麼這個父部門和每一個子部門在表中各占據一行數據。
圖1 該數據庫模型圖顯示了本例中所用到的表
讓訂單數據保持安全的關鍵是,通過SQL Server的表值型函數來保護它,該函數要求使用當前用戶的id作為參數。表值型函數與其它函數一樣可以接受參數,但是返回結果為一個表,而並非一個變量。在這個函數內,通過其安全屬性聯合要保護的表與用戶訪問表,將表的結果限定在與指定用戶id相關的范圍之內。
示例1定義了一個表值型函數,根據UserAccess表中的限制用戶可以查看的內容,返回訂單數量和訂單的總收入。
以下為引用的內容:
CREATE FUNCTION [dbo].[GetOrderSummary]
(
@User_Id VARCHAR(20)
)
RETURNS TABLE
AS
RETURN
(
SELECT count(*) as OrderCount,
sum(OrderTotal) as Receipts
FROM dbo.Orders o
Join dbo.UserAccess ua
On o.Department = ua.Department
WHERE ua.UserID = @User_Id
)
示例1:創建保護訂單數據的表值型函數的SQL語句
而圖2顯示了該函數的執行示例,以及相關表的數據內容,如表1、表2和表3.
圖2 表值型函數執行示例
表1 UserAccess示例表內容
表2 Orders示例表內容
表3 Department表內容
主機網全新上線,買空間、服務器就上主機網,安全有保障!
顯示受保護表內容
通過表值型函數保護了某個表之後,你然後可以通過類似示例2的視圖將其展示給用戶。該視圖調用了上面定義的表值型函數,並使用參數“user”,在SQL Server中這是一個內置函數,返回當前活躍用戶的ID。
以下為引用的內容:
CREATE VIEW [dbo].[OrderSummary]
AS
SELECT OrderCount,
Receipts
FROM dbo.GetOrderSummary(USER)
示例2:創建一個視圖讓安全用戶訪問受保護表的SQL
這個視圖是用戶被授予訪問權限的唯一對象,即使用戶不具有權限使用前面的表值型函數或訪問受保護表,他也可以獲得被許可查看的數據。
實際應用思考
在應用程序開發實際情況中,這種方式往往需要一個以上的屬性來定義用戶行級別權限,而且往往需要對多個表應用安全機制。
另外,不同的數據可能具有不同的安全考慮。舉例來說,一個用戶可能有權限訪問某個區域的銷售結果,但不能訪問匯總薪酬數據。在復雜的實際環境中成功運用這個戰略的關鍵是,收集並確認來自業務的需求,然後根據這些需求來選擇合適的屬性來實施行級別安全。
使用活動目錄的企業,可以按照活動目錄組來使用這種方式獲得安全性,而並一定僅限於使用用戶ID,實現方法非常簡單,只需要在UserAccess表中使用活動目錄組替代用戶ID即可。