本文將介紹當您想要限制資料庫使用者無法讀取某些欄位,可能的幾種作法。
【情境說明】
在論壇上有網友提到如何限制資料行的 select 權限,要達到這個目的方法至少有下列幾種:
- DENY 資料行的 SELECT 權限。
- 拒絕所有對基礎資料表的 SELECT 權限,然後建立 View、Stored Procedure 或 Table-Valued Function 來自訂資料表要回傳的資料行。
本文就針對上述兩種方法進行介紹。
【前置作業】
首先我們先建立一個名稱為 NWUser 的 Database Engine Login 及資料庫使用者,程式碼如下:
1: USE [master]2: GO3: CREATE LOGIN [NWUser] WITH PASSWORD=N'NWUser', DEFAULT_DATABASE=[Northwind], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF4: GO5: USE [Northwind]6: GO7: CREATE USER [NWUser] FOR LOGIN [NWUser]8: GO9: USE [Northwind]10: GO11: ALTER ROLE [db_datareader] ADD MEMBER [NWUser]12: GO
【DENY 資料行的 SELECT 權限】
下列的程式碼示範利用第五列的 DENY 敘述來拒絕 NWUser 存取 Region 資料表的 RegionID 資料行。
1: use Northwind2: go3:
4: --拒絕NWUser查詢Region資料表的RegionID
5: deny select on Region(RegionID) to NWUser6:
7: --切換使用者身分
8: execute as login = 'NWUser'9: go10:
11: --確認使用者身分
12: select USER_NAME() db_user,SUSER_NAME() login_name13:
14: --因為拒絕查詢RegionID欄位,因此SELECT ALL會出現沒有權限的訊息
15: select *16: from Region17: /*18: 訊息 230,層級 14,狀態 1,行 619: 結構描述 'dbo',資料庫 'Northwind',物件 'Region' 的資料行 'RegionID' 沒有 SELECT 權限。20: */21:
22: select RegionDescription23: from Region24:
25: /*26: 回傳 RegionDescription 欄位的內容27: Eastern28: Western29: Northern30: Southern31: */32:
33: --將執行內容切換回最後一個 EXECUTE AS 陳述式的呼叫者。34: revert
35: go
【利用 View、Stored Procedure 或 Table-Valued Function 來自訂資料表要回傳的資料行】
除了上述利用 DENY 敘述來進行權限控管外,您可以利用下列的 T-SQL 來拒絕 NWUser 對 Region 資料表的 SELECT 權限。
1: use Northwind2: go3:
4: --拒絕NWUser查詢Region資料表
5: deny select on Region to NWUser6:
7: --切換使用者身分
8: execute as login = 'NWUser'9: go10:
11: --確認使用者身分
12: select USER_NAME() db_user,SUSER_NAME() login_name13:
14: --因為拒絕查詢Region資料表,因此SELECT時會出現沒有權限的訊息
15: select *16: from Region17: /*18: 訊息 230,層級 14,狀態 1,行 619: 訊息 229,層級 14,狀態 5,行 620: 結構描述 'dbo',資料庫 'Northwind',物件 'Region' 沒有 SELECT 權限。21: */22:
23: --將執行內容切換回最後一個 EXECUTE AS 陳述式的呼叫者。24: revert
25: go
接著您可以建立 View 來回傳您想要提供給 NWUser 查詢的結果,接著再 GRANT NWUSER 對於 View 的 SELECT 權限,程式碼如下:
1: USE Northwind2: GO3:
4: --若 View 已經存在則先移除5: IF OBJECT_ID('dbo.v_GetRegion') IS NOT NULL6: DROP VIEW dbo.v_GetRegion7: GO8:
9: --建立 View 並指定回傳 Region 資料表的 RegionDescription 資料行10: CREATE VIEW dbo.v_GetRegion11: AS12: SELECT RegionDescription13: FROM dbo.Region14:
15: GO16:
17: --拒絕NWUser查詢Region資料表
18: DENY SELECT ON Region TO NWUser19: GO20:
21: --允許NWUser查詢v_GetRegion檢視
22: GRANT SELECT ON dbo.v_GetRegion TO NWUser23: GO24:
25:
26: --切換使用者身分
27: EXECUTE AS LOGIN = 'NWUser'28: GO29:
30: --確認使用者身分
31: SELECT USER_NAME() db_user,SUSER_NAME() login_name32: GO33:
34: --以 NWUSER 身分對 REGION 資料表進行 SELECT,將會出現權限下列的錯誤訊息:35: /*36: 訊息 229,層級 14,狀態 5,行 337: 結構描述 'dbo',資料庫 'Northwind',物件 'Region' 沒有 SELECT 權限。38: */39: SELECT *40: FROM REGION41:
42: GO43:
44: --以 NWUSER 身分對 v_GetRegion 檢視進行 SELECT,則可以正確回傳 Region 資料表的 RegionDescription資料行45: /*46: Eastern47: Western48: Northern49: Southern50: */51:
52: SELECT *53: FROM dbo.v_GetRegion54:
55: GO56:
57: --將執行內容切換回最後一個 EXECUTE AS 陳述式的呼叫者。58: REVERT
59: GO
再者您也可以利用 Stored Procedure 來指定想要回傳的查詢結果,同樣的再針對 Stored Procedure 的 EXECUTE 權限 GRANT 給 NEUSER,程式碼如下:
1: USE Northwind2: GO3:
4: --若 Stored Procedure 已經存在則先移除5: IF OBJECT_ID('dbo.usp_GetRegion') IS NOT NULL6: DROP PROCEDURE dbo.usp_GetRegion7: GO8:
9: --建立 Stored Procedure 並指定回傳 Region 資料表的 RegionDescription 資料行10: CREATE PROCEDURE dbo.usp_GetRegion11: AS12: SET NOCOUNT ON13:
14: SELECT RegionDescription15: FROM dbo.Region16:
17: GO18:
19: --拒絕NWUser查詢Region資料表
20: DENY SELECT ON Region TO NWUser21: GO22:
23: --允許NWUser執行usp_GetRegion預存程序
24: GRANT EXECUTE ON dbo.usp_GetRegion TO NWUser25: GO26:
27:
28: --切換使用者身分
29: EXECUTE AS LOGIN = 'NWUser'30: GO31:
32: --確認使用者身分
33: SELECT USER_NAME() db_user,SUSER_NAME() login_name34: GO35:
36: --以 NWUSER 身分對 REGION 資料表進行 SELECT,將會出現權限下列的錯誤訊息:37: /*38: 訊息 229,層級 14,狀態 5,行 339: 結構描述 'dbo',資料庫 'Northwind',物件 'Region' 沒有 SELECT 權限。40: */41: SELECT *42: FROM REGION43:
44: GO45:
46: --以 NWUSER 身分對執行 usp_GetRegion 預存程序,則可以正確回傳 Region 資料表的 RegionDescription資料行
47: /*48: Eastern49: Western50: Northern51: Southern52: */53:
54: EXECUTE dbo.usp_GetRegion55:
56: GO57:
58: --將執行內容切換回最後一個 EXECUTE AS 陳述式的呼叫者。59: REVERT
60: GO
最後,您也可以透過建立 Table-Valued Function 來自訂對基礎資料表的查詢結果,由於 Table-Valued Function 概念上跟 Table 是相同的,因此在 GRANT 權限時,必須賦予 NWUser 對 Function 的 SELECT 權限。
1: USE Northwind2: GO3:
4: --若 Table-Valued Function 已經存在則先移除5: IF OBJECT_ID('dbo.fn_GetRegion','IF') IS NOT NULL6: DROP FUNCTION dbo.fn_GetRegion7: GO8:
9: --建立 Table-Valued Function 並指定回傳 Region 資料表的 RegionDescription 資料行10: CREATE FUNCTION dbo.fn_GetRegion()11: RETURNS TABLE12: AS13: RETURN (SELECT RegionDescription14: FROM dbo.Region)15: GO16:
17: --拒絕NWUser查詢Region資料表
18: DENY SELECT ON Region TO NWUser19: GO20:
21: --允許NWUser對fn_GetRegion function 進行 SELECT 動作22: GRANT SELECT ON dbo.fn_GetRegion TO NWUser23: GO24:
25:
26: --切換使用者身分
27: EXECUTE AS LOGIN = 'NWUser'28: GO29:
30: --確認使用者身分
31: SELECT USER_NAME() db_user,SUSER_NAME() login_name32: GO33:
34: --以 NWUSER 身分對 REGION 資料表進行 SELECT,將會出現權限下列的錯誤訊息:35: /*36: 訊息 229,層級 14,狀態 5,行 337: 結構描述 'dbo',資料庫 'Northwind',物件 'Region' 沒有 SELECT 權限。38: */39: SELECT *40: FROM REGION41:
42: GO43:
44: --以 NWUSER 身分對SELECT fn_GetRegion Function,則可以正確回傳 Region 資料表的 RegionDescription資料行45: /*46: Eastern47: Western48: Northern49: Southern50: */51:
52: SELECT *53: FROM dbo.fn_GetRegion()54:
55: GO56:
57: --將執行內容切換回最後一個 EXECUTE AS 陳述式的呼叫者。58: REVERT
59: GO
【結論】
上述四種方法都可以達到控管使用者對基礎資料表查詢的目的,但實務上基於維護方便以及安全性考量,比較建議利用 View、Stored Procedure 或是 Table-Valued Function 來對,而避免使用者直接存取基礎資料表。
【參考資料】