如何限制資料庫使用者無法讀取某些欄位

本文將介紹當您想要限制資料庫使用者無法讀取某些欄位,可能的幾種作法。

【情境說明】

論壇上有網友提到如何限制資料行的 select 權限,要達到這個目的方法至少有下列幾種:

  • DENY 資料行的 SELECT 權限。
  • 拒絕所有對基礎資料表的 SELECT 權限,然後建立 View、Stored Procedure 或 Table-Valued Function 來自訂資料表要回傳的資料行。

本文就針對上述兩種方法進行介紹。

【前置作業】

首先我們先建立一個名稱為 NWUser 的 Database Engine Login 及資料庫使用者,程式碼如下:

   1: USE [master]
   2: GO
   3: CREATE LOGIN [NWUser] WITH PASSWORD=N'NWUser', DEFAULT_DATABASE=[Northwind], CHECK_EXPIRATION=OFF, CHECK_POLICY=OFF
   4: GO
   5: USE [Northwind]
   6: GO
   7: CREATE USER [NWUser] FOR LOGIN [NWUser]
   8: GO
   9: USE [Northwind]
  10: GO
  11: ALTER ROLE [db_datareader] ADD MEMBER [NWUser]
  12: GO

【DENY 資料行的 SELECT 權限】

下列的程式碼示範利用第五列的 DENY 敘述來拒絕 NWUser 存取 Region 資料表的 RegionID 資料行。

   1: use Northwind
   2: go
   3:  
   4: --拒絕NWUser查詢Region資料表的RegionID
   5: deny select on Region(RegionID) to NWUser
   6:  
   7: --切換使用者身分
   8: execute as login = 'NWUser'
   9: go
  10:  
  11: --確認使用者身分
  12: select USER_NAME() db_user,SUSER_NAME() login_name
  13:  
  14: --因為拒絕查詢RegionID欄位,因此SELECT ALL會出現沒有權限的訊息
  15: select *
  16: from Region
  17: /*
  18: 訊息 230,層級 14,狀態 1,行 6
  19: 結構描述 'dbo',資料庫 'Northwind',物件 'Region' 的資料行 'RegionID' 沒有 SELECT 權限。
  20: */
  21:  
  22: select RegionDescription
  23: from Region
  24:  
  25: /*
  26: 回傳 RegionDescription 欄位的內容
  27: Eastern                                           
  28: Western                                           
  29: Northern                                          
  30: Southern                                          
  31: */
  32:  
  33: --將執行內容切換回最後一個 EXECUTE AS 陳述式的呼叫者。
  34: revert
  35: go

【利用 View、Stored Procedure 或 Table-Valued Function 來自訂資料表要回傳的資料行】

除了上述利用 DENY 敘述來進行權限控管外,您可以利用下列的 T-SQL 來拒絕 NWUser 對 Region 資料表的 SELECT 權限。

   1: use Northwind
   2: go
   3:  
   4: --拒絕NWUser查詢Region資料表
   5: deny select on Region to NWUser
   6:  
   7: --切換使用者身分
   8: execute as login = 'NWUser'
   9: go
  10:  
  11: --確認使用者身分
  12: select USER_NAME() db_user,SUSER_NAME() login_name
  13:  
  14: --因為拒絕查詢Region資料表,因此SELECT時會出現沒有權限的訊息
  15: select *
  16: from Region
  17: /*
  18: 訊息 230,層級 14,狀態 1,行 6
  19: 訊息 229,層級 14,狀態 5,行 6
  20: 結構描述 'dbo',資料庫 'Northwind',物件 'Region' 沒有 SELECT 權限。
  21: */
  22:  
  23: --將執行內容切換回最後一個 EXECUTE AS 陳述式的呼叫者。
  24: revert
  25: go

 

接著您可以建立 View 來回傳您想要提供給 NWUser 查詢的結果,接著再 GRANT NWUSER 對於 View 的 SELECT 權限,程式碼如下:

   1: USE Northwind
   2: GO
   3:  
   4: --若 View 已經存在則先移除
   5: IF OBJECT_ID('dbo.v_GetRegion') IS NOT NULL
   6:     DROP VIEW dbo.v_GetRegion
   7: GO
   8:  
   9: --建立 View 並指定回傳 Region 資料表的 RegionDescription 資料行
  10: CREATE VIEW dbo.v_GetRegion
  11: AS
  12:     SELECT RegionDescription
  13:     FROM dbo.Region
  14:  
  15: GO
  16:  
  17: --拒絕NWUser查詢Region資料表
  18: DENY SELECT ON Region TO NWUser
  19: GO
  20:  
  21: --允許NWUser查詢v_GetRegion檢視
  22: GRANT SELECT ON dbo.v_GetRegion TO NWUser
  23: GO
  24:  
  25:  
  26: --切換使用者身分
  27: EXECUTE AS LOGIN = 'NWUser'
  28: GO
  29:  
  30: --確認使用者身分
  31: SELECT USER_NAME() db_user,SUSER_NAME() login_name
  32: GO
  33:  
  34: --以 NWUSER 身分對 REGION 資料表進行 SELECT,將會出現權限下列的錯誤訊息:
  35: /*
  36: 訊息 229,層級 14,狀態 5,行 3
  37: 結構描述 'dbo',資料庫 'Northwind',物件 'Region' 沒有 SELECT 權限。
  38: */
  39: SELECT *
  40: FROM REGION
  41:  
  42: GO
  43:  
  44: --以 NWUSER 身分對 v_GetRegion 檢視進行 SELECT,則可以正確回傳 Region 資料表的 RegionDescription資料行
  45: /*
  46: Eastern                                           
  47: Western                                           
  48: Northern                                          
  49: Southern                                          
  50: */
  51:  
  52: SELECT *
  53: FROM dbo.v_GetRegion
  54:  
  55: GO
  56:  
  57: --將執行內容切換回最後一個 EXECUTE AS 陳述式的呼叫者。
  58: REVERT
  59: GO

 

再者您也可以利用 Stored Procedure 來指定想要回傳的查詢結果,同樣的再針對 Stored Procedure 的 EXECUTE 權限 GRANT 給 NEUSER,程式碼如下:

   1: USE Northwind
   2: GO
   3:  
   4: --若 Stored Procedure 已經存在則先移除
   5: IF OBJECT_ID('dbo.usp_GetRegion') IS NOT NULL
   6:     DROP PROCEDURE dbo.usp_GetRegion
   7: GO
   8:  
   9: --建立 Stored Procedure 並指定回傳 Region 資料表的 RegionDescription 資料行
  10: CREATE PROCEDURE dbo.usp_GetRegion
  11: AS
  12:     SET NOCOUNT ON
  13:  
  14:     SELECT RegionDescription
  15:     FROM dbo.Region
  16:  
  17: GO
  18:  
  19: --拒絕NWUser查詢Region資料表
  20: DENY SELECT ON Region TO NWUser
  21: GO
  22:  
  23: --允許NWUser執行usp_GetRegion預存程序
  24: GRANT EXECUTE ON dbo.usp_GetRegion TO NWUser
  25: GO
  26:  
  27:  
  28: --切換使用者身分
  29: EXECUTE AS LOGIN = 'NWUser'
  30: GO
  31:  
  32: --確認使用者身分
  33: SELECT USER_NAME() db_user,SUSER_NAME() login_name
  34: GO
  35:  
  36: --以 NWUSER 身分對 REGION 資料表進行 SELECT,將會出現權限下列的錯誤訊息:
  37: /*
  38: 訊息 229,層級 14,狀態 5,行 3
  39: 結構描述 'dbo',資料庫 'Northwind',物件 'Region' 沒有 SELECT 權限。
  40: */
  41: SELECT *
  42: FROM REGION
  43:  
  44: GO
  45:  
  46: --以 NWUSER 身分對執行 usp_GetRegion 預存程序,則可以正確回傳 Region 資料表的 RegionDescription資料行
  47: /*
  48: Eastern                                           
  49: Western                                           
  50: Northern                                          
  51: Southern                                          
  52: */
  53:  
  54: EXECUTE dbo.usp_GetRegion
  55:  
  56: GO
  57:  
  58: --將執行內容切換回最後一個 EXECUTE AS 陳述式的呼叫者。
  59: REVERT
  60: GO

 

最後,您也可以透過建立 Table-Valued Function 來自訂對基礎資料表的查詢結果,由於 Table-Valued Function 概念上跟 Table 是相同的,因此在 GRANT 權限時,必須賦予 NWUser 對 Function 的 SELECT 權限。

   1: USE Northwind
   2: GO
   3:  
   4: --若 Table-Valued Function 已經存在則先移除
   5: IF OBJECT_ID('dbo.fn_GetRegion','IF') IS NOT NULL
   6:     DROP FUNCTION dbo.fn_GetRegion
   7: GO
   8:  
   9: --建立 Table-Valued Function 並指定回傳 Region 資料表的 RegionDescription 資料行
  10: CREATE FUNCTION dbo.fn_GetRegion()
  11: RETURNS TABLE
  12: AS
  13:    RETURN (SELECT RegionDescription
  14:     FROM dbo.Region)
  15: GO
  16:  
  17: --拒絕NWUser查詢Region資料表
  18: DENY SELECT ON Region TO NWUser
  19: GO
  20:  
  21: --允許NWUser對fn_GetRegion function 進行 SELECT 動作
  22: GRANT SELECT ON dbo.fn_GetRegion TO NWUser
  23: GO
  24:  
  25:  
  26: --切換使用者身分
  27: EXECUTE AS LOGIN = 'NWUser'
  28: GO
  29:  
  30: --確認使用者身分
  31: SELECT USER_NAME() db_user,SUSER_NAME() login_name
  32: GO
  33:  
  34: --以 NWUSER 身分對 REGION 資料表進行 SELECT,將會出現權限下列的錯誤訊息:
  35: /*
  36: 訊息 229,層級 14,狀態 5,行 3
  37: 結構描述 'dbo',資料庫 'Northwind',物件 'Region' 沒有 SELECT 權限。
  38: */
  39: SELECT *
  40: FROM REGION
  41:  
  42: GO
  43:  
  44: --以 NWUSER 身分對SELECT fn_GetRegion Function,則可以正確回傳 Region 資料表的 RegionDescription資料行
  45: /*
  46: Eastern                                           
  47: Western                                           
  48: Northern                                          
  49: Southern                                          
  50: */
  51:  
  52: SELECT *
  53: FROM dbo.fn_GetRegion()
  54:  
  55: GO
  56:  
  57: --將執行內容切換回最後一個 EXECUTE AS 陳述式的呼叫者。
  58: REVERT
  59: GO

【結論】

上述四種方法都可以達到控管使用者對基礎資料表查詢的目的,但實務上基於維護方便以及安全性考量,比較建議利用 View、Stored Procedure 或是 Table-Valued Function 來對,而避免使用者直接存取基礎資料表。

【參考資料】