本文將介紹當您想要限制資料庫使用者無法讀取某些欄位,可能的幾種作法。
【情境說明】
在論壇上有網友提到如何限制資料行的 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資料表的RegionID5: 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: revert35: 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: revert25: 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: REVERT59: 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: REVERT60: 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: REVERT59: GO
【結論】
上述四種方法都可以達到控管使用者對基礎資料表查詢的目的,但實務上基於維護方便以及安全性考量,比較建議利用 View、Stored Procedure 或是 Table-Valued Function 來對,而避免使用者直接存取基礎資料表。
【參考資料】